mirror of
https://gitee.com/ThingsGateway/ThingsGateway.git
synced 2025-10-28 22:23:59 +08:00
整理文件
This commit is contained in:
15
framework/demo/Directory.Build.props
Normal file
15
framework/demo/Directory.Build.props
Normal file
@@ -0,0 +1,15 @@
|
||||
<Project>
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net6.0;net8.0;</TargetFrameworks>
|
||||
<Version>4.0.0.0</Version>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<Authors>Diego</Authors>
|
||||
<Product>ThingsGateway</Product>
|
||||
<Copyright>© 2023-present Diego</Copyright>
|
||||
<RepositoryUrl>https://gitee.com/diego2098/ThingsGateway</RepositoryUrl>
|
||||
<SatelliteResourceLanguages>zh-Hans</SatelliteResourceLanguages>
|
||||
<GenerateDocumentationFile>False</GenerateDocumentationFile>
|
||||
</PropertyGroup>
|
||||
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,16 @@
|
||||
#region copyright
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
#endregion
|
||||
|
||||
global using System;
|
||||
|
||||
global using ThingsGateway.Components;
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
#region copyright
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
#endregion
|
||||
|
||||
using Photino.Blazor;
|
||||
|
||||
namespace ThingsGateway.Foundation.Demo;
|
||||
|
||||
internal class Program
|
||||
{
|
||||
[STAThread]
|
||||
private static void Main(string[] args)
|
||||
{
|
||||
System.IO.Directory.SetCurrentDirectory(AppContext.BaseDirectory);
|
||||
|
||||
var appBuilder = PhotinoBlazorAppBuilder.CreateDefault(args);
|
||||
|
||||
Serve.RunNative();
|
||||
|
||||
appBuilder.RootComponents.Add<App>("#app");
|
||||
|
||||
appBuilder.Services.ThingsGatewayComponentsConfigureServices();
|
||||
var app = appBuilder.Build();
|
||||
app.MainWindow.SetTitle("ThingsGateway.Foundation.Demo");
|
||||
app.MainWindow.SetIconFile("wwwroot/favicon.ico");
|
||||
AppDomain.CurrentDomain.UnhandledException += (sender, error) =>
|
||||
{
|
||||
};
|
||||
app.Run();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Razor">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<TargetFrameworks>net6.0;net8.0;</TargetFrameworks>
|
||||
<ApplicationIcon>favicon.ico</ApplicationIcon>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Remove="favicon.ico" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="favicon.ico">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
|
||||
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Photino.Blazor" Version="2.6.0" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\ThingsGateway.Foundation.Demo.Rcl\ThingsGateway.Foundation.Demo.Rcl.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
<ItemGroup>
|
||||
<Content Update="wwwroot\favicon.ico">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="wwwroot\favicon.png">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="wwwroot\index.html">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
|
||||
</Project>
|
||||
BIN
framework/demo/ThingsGateway.Foundation.Demo.Photino/favicon.ico
Normal file
BIN
framework/demo/ThingsGateway.Foundation.Demo.Photino/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.2 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 4.2 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 10 KiB |
@@ -0,0 +1,41 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover" />
|
||||
<title>ThingsGateway.Foundation.Demo</title>
|
||||
<base href="/" />
|
||||
|
||||
<link rel="icon" href="favicon.ico" type="image/x-icon">
|
||||
|
||||
|
||||
<link href="_content/Masa.Blazor/css/masa-blazor.min.css" rel="stylesheet" />
|
||||
<link href="_content/ThingsGateway.Components/css/materialdesign/v7.1.96/css/materialdesignicons.min.css" rel="stylesheet">
|
||||
<link href="_content/ThingsGateway.Components/css/material/icons.css" rel="stylesheet">
|
||||
<link href="_content/ThingsGateway.Components/css/fontawesome/v6.4.0/css/all.min.css" rel="stylesheet">
|
||||
<link href="_content/ThingsGateway.Components/style/custom.css" rel="stylesheet">
|
||||
<link href="_content/ThingsGateway.Components/prism/prism-material-dark-for-masa.css" rel="stylesheet">
|
||||
<link href="_content/ThingsGateway.Components/prism/prism-line-highlight.min.css" rel="stylesheet">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
|
||||
<div id="blazor-error-ui">
|
||||
<span>
|
||||
<environment include="Staging,Production">
|
||||
An error has occurred. This application may no longer respond until reloaded.
|
||||
</environment>
|
||||
<environment include="Development">
|
||||
An unhandled exception has occurred. See browser dev tools for details.
|
||||
</environment>
|
||||
</span>
|
||||
<a href="" class="reload">Reload</a>
|
||||
<a class="dismiss">🗙</a>
|
||||
</div>
|
||||
|
||||
<script src="_framework/blazor.webview.js" autostart="true"></script>
|
||||
<script src="_content/ThingsGateway.Components/prism/prism.min.js"></script>
|
||||
<script src="_content/BlazorComponent/js/blazor-component.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
26
framework/demo/ThingsGateway.Foundation.Demo.Rcl/App.razor
Normal file
26
framework/demo/ThingsGateway.Foundation.Demo.Rcl/App.razor
Normal file
@@ -0,0 +1,26 @@
|
||||
@*
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
*@
|
||||
|
||||
@namespace ThingsGateway.Foundation.Demo
|
||||
|
||||
<Router AppAssembly="@typeof(App).Assembly">
|
||||
<Found Context="routeData">
|
||||
<RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
|
||||
<FocusOnNavigate RouteData="@routeData" Selector="h1" />
|
||||
</Found>
|
||||
<NotFound>
|
||||
<LayoutView Layout="@typeof(MainLayout)">
|
||||
<p role="alert">Sorry, there's nothing at this address.</p>
|
||||
</LayoutView>
|
||||
</NotFound>
|
||||
</Router>
|
||||
|
||||
@@ -0,0 +1,144 @@
|
||||
#region copyright
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
#endregion
|
||||
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
namespace ThingsGateway.Foundation.Demo;
|
||||
using LogLevel = Microsoft.Extensions.Logging.LogLevel;
|
||||
/// <summary>
|
||||
/// 调试UI
|
||||
/// </summary>
|
||||
public abstract class DriverDebugUIBase : ComponentBase, IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// 日志缓存
|
||||
/// </summary>
|
||||
public ConcurrentLinkedList<(LogLevel level, string message)> Messages = new();
|
||||
|
||||
private PeriodicTimer _periodicTimer = new(TimeSpan.FromSeconds(1));
|
||||
|
||||
/// <inheritdoc/>
|
||||
~DriverDebugUIBase()
|
||||
{
|
||||
this.SafeDispose();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 变量地址
|
||||
/// </summary>
|
||||
public virtual string Address { get; set; } = "40001";
|
||||
|
||||
/// <summary>
|
||||
/// <inheritdoc/>
|
||||
/// </summary>
|
||||
[Inject]
|
||||
public InitTimezone InitTimezone { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 长度
|
||||
/// </summary>
|
||||
public virtual int Length { get; set; } = 1;
|
||||
|
||||
/// <summary>
|
||||
/// 默认读写设备
|
||||
/// </summary>
|
||||
public virtual IReadWrite Plc { get; set; }
|
||||
/// <summary>
|
||||
/// 写入值
|
||||
/// </summary>
|
||||
public virtual string WriteValue { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 数据类型
|
||||
/// </summary>
|
||||
protected virtual DataTypeEnum DataTypeEnum { get; set; } = DataTypeEnum.Int16;
|
||||
/// <inheritdoc/>
|
||||
public virtual void Dispose()
|
||||
{
|
||||
_periodicTimer?.Dispose();
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public void LogOut(ThingsGateway.Foundation.Core.LogLevel logLevel, object source, string message, Exception exception)
|
||||
{
|
||||
Messages.Add(((LogLevel)logLevel,
|
||||
$"{DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(InitTimezone.TimezoneOffset)} - {message} {exception}"));
|
||||
if (Messages.Count > 2500)
|
||||
{
|
||||
Messages.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public virtual async Task ReadAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
var data = await Plc.ReadAsync(Address, Length, DataTypeEnum);
|
||||
if (data.IsSuccess)
|
||||
{
|
||||
Messages.Add((LogLevel.Information,
|
||||
$"{DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(InitTimezone.TimezoneOffset)} - 对应类型值:{Environment.NewLine}{data.Content.ToJsonString(true)} "));
|
||||
}
|
||||
else
|
||||
{
|
||||
Messages.Add((LogLevel.Warning,
|
||||
$"{DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(InitTimezone.TimezoneOffset)} - {data.Message}"));
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Messages.Add((LogLevel.Error,
|
||||
$"{DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(InitTimezone.TimezoneOffset)} - 错误:{ex}"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public virtual async Task WriteAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
var data = await Plc.WriteAsync(Address, WriteValue, Length, DataTypeEnum);
|
||||
if (data.IsSuccess)
|
||||
{
|
||||
Messages.Add((LogLevel.Information,
|
||||
$"{DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(InitTimezone.TimezoneOffset)} - {data.Message}"));
|
||||
}
|
||||
else
|
||||
{
|
||||
Messages.Add((LogLevel.Warning,
|
||||
$"{DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(InitTimezone.TimezoneOffset)} - {data.Message}"));
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Messages.Add((LogLevel.Error,
|
||||
$"{DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(InitTimezone.TimezoneOffset)} - 写入前失败:{ex}"));
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
_ = RunTimerAsync();
|
||||
base.OnInitialized();
|
||||
}
|
||||
private async Task RunTimerAsync()
|
||||
{
|
||||
while (await _periodicTimer.WaitForNextTickAsync())
|
||||
{
|
||||
await InvokeAsync(StateHasChanged);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,205 @@
|
||||
@*
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
*@
|
||||
|
||||
@using BlazorComponent;
|
||||
@using Microsoft.AspNetCore.Components.Web;
|
||||
@using Microsoft.JSInterop;
|
||||
@using ThingsGateway.Foundation.Core;
|
||||
@using Masa.Blazor;
|
||||
@namespace ThingsGateway.Foundation.Demo
|
||||
@inherits DriverDebugUIBase
|
||||
|
||||
<MCard Elevation="1" Rounded="false" Class=" pa-2" Style="width:100%">
|
||||
<MRow Class="my-1" Justify="JustifyTypes.Start" Align="AlignTypes.Start" NoGutters>
|
||||
|
||||
<MCol Md="5">
|
||||
<MTabs @bind-Value="tab" Class="ma-2">
|
||||
<MTab Value=1> 读写测试 </MTab>
|
||||
<MTab Value=2> 特殊功能 </MTab>
|
||||
<MTab Value=3> 代码示例 </MTab>
|
||||
</MTabs>
|
||||
|
||||
<MTabsItems Value="tab">
|
||||
<MTabItem Value="1">
|
||||
@if (tab == 1)
|
||||
{
|
||||
@if (ReadWriteContent == null)
|
||||
{
|
||||
|
||||
<div class="my-1 py-1">
|
||||
<MTooltip Bottom Context="tip">
|
||||
<ActivatorContent>
|
||||
<MTextarea Class="mx-1 my-1" Label="变量地址" @attributes="@tip.Attrs" Dense Outlined HideDetails="@("auto")" @bind-Value=@Address />
|
||||
</ActivatorContent>
|
||||
<ChildContent>
|
||||
<span style="white-space: pre-wrap;">@Plc?.GetAddressDescription()</span>
|
||||
</ChildContent>
|
||||
</MTooltip>
|
||||
<MRow Class="my-1" Justify="JustifyTypes.Start" Align="AlignTypes.Start" NoGutters>
|
||||
<MTextField Class="mx-1 my-1" Style="max-width:200px" 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.Description)
|
||||
ItemValue=@(u =>(DataTypeEnum)u.Value)
|
||||
HideDetails=@("auto") Height="30"
|
||||
Dense>
|
||||
</MSelect>
|
||||
</MRow>
|
||||
|
||||
<MButton Class="mx-1 my-1" Color="primary" OnClick="ReadAsync">
|
||||
读取
|
||||
</MButton>
|
||||
<MTextarea Class="mx-1 mt-3 my-1" Label="值" Dense Outlined HideDetails="@("auto")" @bind-Value=@WriteValue />
|
||||
<MButton Class="mx-1 my-1" Color="primary" OnClick="WriteAsync">
|
||||
写入
|
||||
</MButton>
|
||||
|
||||
</div>
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
@ReadWriteContent
|
||||
}
|
||||
}
|
||||
</MTabItem>
|
||||
|
||||
<MTabItem Value="2">
|
||||
@if (tab == 2)
|
||||
{
|
||||
@if (ShowDefaultOtherContent)
|
||||
{
|
||||
<MSubheader>
|
||||
连读打包
|
||||
</MSubheader>
|
||||
<MContainer>
|
||||
|
||||
@foreach (var item in DeviceVariableRunTimes)
|
||||
{
|
||||
<MRow Dense Align="AlignTypes.Center">
|
||||
<MTextField Class="ma-1" Outlined Style="min-width:100px" Label=@(item.Description(x => x.Address)) Dense HideDetails="@("auto")" @bind-Value=@item.Address></MTextField>
|
||||
<MSelect Class="mx-1 my-1" Style="max-width:120px" @bind-Value="item.DataTypeEnum" Outlined Label=@(item.Description(x => x.DataTypeEnum))
|
||||
Items=@(typeof(DataTypeEnum).GetEnumList())
|
||||
MenuProps="@(props => { props.Auto = true; props.OffsetY = true; })"
|
||||
ItemText=@((u) =>u.Description)
|
||||
ItemValue=@(u =>(DataTypeEnum)u.Value)
|
||||
HideDetails=@("auto") Height="30"
|
||||
Dense>
|
||||
</MSelect>
|
||||
|
||||
<MTextField Class="ma-1" Outlined Style="max-width:100px" Label=@(item.Description(x => x.IntervalTime)) Dense HideDetails="@("auto")" @bind-Value=@item.IntervalTime></MTextField>
|
||||
|
||||
<MTextField Class="ma-1" Outlined Style="max-width:100px" Label=实时值 Readonly ClearIcon="" Dense HideDetails="@("auto")" Value=item.Value?.ToJsonString()></MTextField>
|
||||
|
||||
</MRow>
|
||||
}
|
||||
<MRow Dense>
|
||||
<MTextField Class="ma-1" Outlined Style="max-width:100px" Label="打包长度" Dense HideDetails="@("auto")" @bind-Value=@MaxPack></MTextField>
|
||||
<MButton Class="ma-1" Color="primary" OnClick="MulReadAsync">
|
||||
读取
|
||||
</MButton>
|
||||
</MRow>
|
||||
</MContainer>
|
||||
|
||||
}
|
||||
|
||||
@if (OtherContent != null)
|
||||
{
|
||||
<MSheet Style="height:100%;overflow-y:auto">
|
||||
@OtherContent
|
||||
</MSheet>
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
</MTabItem>
|
||||
|
||||
|
||||
<MTabItem Value="3">
|
||||
@if (tab == 3)
|
||||
{
|
||||
@if (CodeContent != null)
|
||||
@CodeContent
|
||||
else
|
||||
{
|
||||
|
||||
<MRow Align="AlignTypes.Center">
|
||||
<MContainer>
|
||||
|
||||
<MItemGroup @bind-Value="_selected" Class="shrink mr-6" Mandatory>
|
||||
@{
|
||||
|
||||
int index = 0;
|
||||
|
||||
}
|
||||
<MRow>
|
||||
|
||||
@foreach (var item in Sections)
|
||||
{
|
||||
<MItem Value="@(index++)">
|
||||
<div>
|
||||
<MButton IsActive="@context.Active" Icon OnClick="@context.Toggle">
|
||||
<MIcon>mdi-record</MIcon>
|
||||
</MButton>
|
||||
</div>
|
||||
</MItem>
|
||||
}
|
||||
</MRow>
|
||||
</MItemGroup>
|
||||
</MContainer>
|
||||
|
||||
<MCol>
|
||||
<MWindow Value="_selected" Vertical Class="elevation-1 grey lighten-5 rounded-b" Style=@($"height:450px;overflow:auto")>
|
||||
@{
|
||||
int index = 0;
|
||||
}
|
||||
@foreach (var item in Sections)
|
||||
{
|
||||
<MWindowItem Value="@(index++)">
|
||||
<AppCode RoundedTop0 Code="@item.Code" Language="@item.Language" />
|
||||
</MWindowItem>
|
||||
}
|
||||
</MWindow>
|
||||
</MCol>
|
||||
</MRow>
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
</MTabItem>
|
||||
</MTabsItems>
|
||||
|
||||
</MCol>
|
||||
|
||||
<MCol Md="7">
|
||||
<MCard Style="overflow-y:auto;width:100%" Elevation="0" Flat Class="ml-4">
|
||||
<ConsoleTxt Messages="Messages" Height=500></ConsoleTxt>
|
||||
</MCard>
|
||||
</MCol>
|
||||
|
||||
</MRow>
|
||||
|
||||
</MCard>
|
||||
|
||||
|
||||
@code {
|
||||
StringNumber tab;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,238 @@
|
||||
#region copyright
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
#endregion
|
||||
|
||||
using BlazorComponent;
|
||||
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace ThingsGateway.Foundation.Demo;
|
||||
|
||||
|
||||
/// <inheritdoc/>
|
||||
public partial class DriverDebugUIPage : DriverDebugUIBase
|
||||
{
|
||||
/// <summary>
|
||||
/// DeviceVariableRunTimes
|
||||
/// </summary>
|
||||
public List<DeviceVariableRunTime> DeviceVariableRunTimes;
|
||||
/// <summary>
|
||||
/// MaxPack
|
||||
/// </summary>
|
||||
public int MaxPack = 100;
|
||||
private StringNumber _selected = 0;
|
||||
|
||||
/// <inheritdoc/>
|
||||
~DriverDebugUIPage()
|
||||
{
|
||||
this.SafeDispose();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 自定义模板
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public RenderFragment CodeContent { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 自定义模板
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public RenderFragment OtherContent { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// <inheritdoc/>
|
||||
/// </summary>
|
||||
public override IReadWrite Plc { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 自定义模板
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public RenderFragment ReadWriteContent { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Sections
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public List<(string Code, string Language)> Sections { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// ShowDefaultOtherContent
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public bool ShowDefaultOtherContent { get; set; } = true;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void Dispose()
|
||||
{
|
||||
Plc?.SafeDispose();
|
||||
base.Dispose();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// MulReadAsync
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public async Task MulReadAsync()
|
||||
{
|
||||
var deviceVariableSourceReads = Plc.LoadSourceRead<DeviceVariableSourceRead, DeviceVariableRunTime>(DeviceVariableRunTimes, MaxPack, 1000);
|
||||
foreach (var item in deviceVariableSourceReads)
|
||||
{
|
||||
var result = await Plc.ReadAsync(item.Address, item.Length);
|
||||
if (result.IsSuccess)
|
||||
{
|
||||
try
|
||||
{
|
||||
item.DeviceVariableRunTimes.PraseStructContent(Plc, result.Content);
|
||||
Messages.Add((Microsoft.Extensions.Logging.LogLevel.Information, DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(InitTimezone.TimezoneOffset) + " - " + result.Content.ToHexString(' ')));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Messages.Add((Microsoft.Extensions.Logging.LogLevel.Warning, DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(InitTimezone.TimezoneOffset) + " - " + ex));
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
Messages.Add((Microsoft.Extensions.Logging.LogLevel.Warning, DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(InitTimezone.TimezoneOffset) + " - " + result.Message));
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// <inheritdoc/>
|
||||
/// </summary>
|
||||
/// <param name="firstRender"></param>
|
||||
protected override void OnAfterRender(bool firstRender)
|
||||
{
|
||||
base.OnAfterRender(firstRender);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
DeviceVariableRunTimes = new()
|
||||
{
|
||||
new DeviceVariableRunTime()
|
||||
{
|
||||
DataTypeEnum=DataTypeEnum.Int16,
|
||||
Address="40001",
|
||||
IntervalTime=1000,
|
||||
},
|
||||
new DeviceVariableRunTime()
|
||||
{
|
||||
DataTypeEnum=DataTypeEnum.Int16,
|
||||
Address="40011",
|
||||
IntervalTime=1000,
|
||||
},
|
||||
new DeviceVariableRunTime()
|
||||
{
|
||||
DataTypeEnum=DataTypeEnum.Int16,
|
||||
Address="40031",
|
||||
IntervalTime=1000,
|
||||
},
|
||||
new DeviceVariableRunTime()
|
||||
{
|
||||
DataTypeEnum=DataTypeEnum.Int16,
|
||||
Address="40101",
|
||||
IntervalTime=1000,
|
||||
},
|
||||
};
|
||||
Sections.Add((
|
||||
"""
|
||||
/// <inheritdoc/>
|
||||
public class DeviceVariableSourceRead : IDeviceVariableSourceRead<DeviceVariableRunTime>
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public TimerTick TimerTick { get; set; }
|
||||
/// <inheritdoc/>
|
||||
public string Address { get; set; }
|
||||
/// <inheritdoc/>
|
||||
public int Length { get; set; }
|
||||
/// <inheritdoc/>
|
||||
public List<DeviceVariableRunTime> DeviceVariableRunTimes { get; set; } = new List<DeviceVariableRunTime>();
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public class DeviceVariableRunTime : IDeviceVariableRunTime
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
[Description("读取间隔")]
|
||||
public int IntervalTime { get; set; }
|
||||
/// <inheritdoc/>
|
||||
[Description("变量地址")]
|
||||
public string Address { get; set; }
|
||||
/// <inheritdoc/>
|
||||
public int Index { get; set; }
|
||||
/// <inheritdoc/>
|
||||
public IThingsGatewayBitConverter ThingsGatewayBitConverter { get; set; }
|
||||
/// <inheritdoc/>
|
||||
[Description("数据类型")]
|
||||
public DataTypeEnum DataTypeEnum { get; set; }
|
||||
/// <inheritdoc/>
|
||||
[Description("实时值")]
|
||||
public object Value { get; set; }
|
||||
/// <inheritdoc/>
|
||||
public OperResult SetValue(object value)
|
||||
{
|
||||
Value = value;
|
||||
return OperResult.CreateSuccessResult();
|
||||
}
|
||||
}
|
||||
public List<DeviceVariableRunTime> DeviceVariableRunTimes;
|
||||
|
||||
private static async Task ModbusClientAsync(IReadWrite plc)
|
||||
{
|
||||
DeviceVariableRunTimes = new()
|
||||
{
|
||||
new DeviceVariableRunTime()
|
||||
{
|
||||
DataTypeEnum=DataTypeEnum.Int16,
|
||||
Address="40001",
|
||||
IntervalTime=1000,
|
||||
},
|
||||
new DeviceVariableRunTime()
|
||||
{
|
||||
DataTypeEnum=DataTypeEnum.Int16,
|
||||
Address="40011",
|
||||
IntervalTime=1000,
|
||||
},
|
||||
new DeviceVariableRunTime()
|
||||
{
|
||||
DataTypeEnum=DataTypeEnum.Int16,
|
||||
Address="40031",
|
||||
IntervalTime=1000,
|
||||
},
|
||||
new DeviceVariableRunTime()
|
||||
{
|
||||
DataTypeEnum=DataTypeEnum.Int16,
|
||||
Address="40101",
|
||||
IntervalTime=1000,
|
||||
},
|
||||
};
|
||||
|
||||
#region 连读
|
||||
var deviceVariableSourceReads = Plc.LoadSourceRead<DeviceVariableSourceRead, DeviceVariableRunTime>(DeviceVariableRunTimes, MaxPack);
|
||||
foreach (var item in deviceVariableSourceReads)
|
||||
{
|
||||
var result = await Plc.ReadAsync(item.Address, item.Length);
|
||||
if (result.IsSuccess)
|
||||
{
|
||||
item.DeviceVariableRunTimes.PraseStructContent(result.Content);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
}
|
||||
|
||||
""", "csharp"));
|
||||
base.OnInitialized();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
@*
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
*@
|
||||
|
||||
@namespace ThingsGateway.Foundation.Demo
|
||||
@using BlazorComponent;
|
||||
@using Microsoft.AspNetCore.Components.Web;
|
||||
@using System.IO.Ports;
|
||||
@using Masa.Blazor
|
||||
<MCard Flat Class="pa-2 my-1" Style="width:100%">
|
||||
<div class="mb-4">通道配置</div>
|
||||
<MRow Justify="JustifyTypes.Start" Align="AlignTypes.Center">
|
||||
<MTextField Style="max-width:100px" Class="ma-1" Outlined Label=@(_serialProperty.Description(x => x.PortName)) Dense HideDetails="@("auto")" @bind-Value=@_serialProperty.PortName />
|
||||
<MTextField Style="max-width:100px" Class="ma-1" Outlined Label=@(_serialProperty.Description(x => x.BaudRate)) Dense HideDetails="@("auto")" @bind-Value=@_serialProperty.BaudRate />
|
||||
<MTextField Style="max-width:100px" Class="ma-1" Outlined Label=@(_serialProperty.Description(x => x.DataBits)) Dense HideDetails="@("auto")" @bind-Value=@_serialProperty.DataBits />
|
||||
<MSelect Class="ma-1 " Style="max-width:200px" Outlined @bind-Value="_serialProperty.Parity" Label="@(_serialProperty.Description(x => x.Parity))"
|
||||
Items=@(typeof(Parity).GetEnumList())
|
||||
MenuProps="@(props => { props.Auto = true; props.OffsetY = true; })"
|
||||
ItemText=@((u) =>u.Description)
|
||||
ItemValue=@(u =>(Parity)u.Value)
|
||||
HideDetails=@("auto") Height="30"
|
||||
Dense>
|
||||
</MSelect>
|
||||
<MSelect Class="ma-1 " Style="max-width:200px" Outlined @bind-Value="_serialProperty.StopBits" Label="@(_serialProperty.Description(x => x.StopBits))"
|
||||
Items=@(typeof(StopBits).GetEnumList())
|
||||
MenuProps="@(props => { props.Auto = true; props.OffsetY = true; })"
|
||||
ItemText=@((u) =>u.Description)
|
||||
ItemValue=@(u =>(StopBits)u.Value)
|
||||
HideDetails=@("auto") Height="30"
|
||||
Dense>
|
||||
</MSelect>
|
||||
<MButton Class="ma-1" OnClick=@ConnectAsync Color="primary">
|
||||
连接
|
||||
</MButton>
|
||||
<MButton Class="ma-1" OnClick=@DisConnect Color="red">
|
||||
断开
|
||||
</MButton>
|
||||
</MRow>
|
||||
</MCard>
|
||||
@@ -0,0 +1,104 @@
|
||||
#region copyright
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
#endregion
|
||||
|
||||
namespace ThingsGateway.Foundation.Demo;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public partial class SerialSessionPage : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// 日志输出
|
||||
/// </summary>
|
||||
public Action<LogLevel, object, string, Exception> LogAction;
|
||||
|
||||
private readonly SerialProperty _serialProperty = new();
|
||||
private TouchSocketConfig _config;
|
||||
private SerialSession _serialSession { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// <inheritdoc/>
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
_serialSession.SafeDispose();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取对象
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public SerialSession GetSerialSession()
|
||||
{
|
||||
_config ??= new TouchSocketConfig();
|
||||
var LogMessage = new LoggerGroup() { LogLevel = LogLevel.Trace };
|
||||
LogMessage.AddLogger(new EasyLogger(LogOut) { LogLevel = LogLevel.Trace });
|
||||
_config.ConfigureContainer(a => a.RegisterSingleton<ILog>(LogMessage));
|
||||
_config.SetSerialProperty(_serialProperty);
|
||||
//载入配置
|
||||
_serialSession.Setup(_config);
|
||||
return _serialSession;
|
||||
}
|
||||
internal void StateHasChangedAsync()
|
||||
{
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <inheritdoc/>
|
||||
/// </summary>
|
||||
/// <param name="firstRender"></param>
|
||||
protected override void OnAfterRender(bool firstRender)
|
||||
{
|
||||
if (firstRender)
|
||||
{
|
||||
var LogMessage = new LoggerGroup() { LogLevel = LogLevel.Trace };
|
||||
LogMessage.AddLogger(new EasyLogger(LogOut) { LogLevel = LogLevel.Trace });
|
||||
_config.ConfigureContainer(a => a.RegisterSingleton<ILog>(LogMessage));
|
||||
_serialSession.Setup(_config);
|
||||
}
|
||||
base.OnAfterRender(firstRender);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
_config ??= new TouchSocketConfig();
|
||||
|
||||
base.OnInitialized();
|
||||
}
|
||||
|
||||
private async Task ConnectAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
_serialSession.Close();
|
||||
await GetSerialSession().ConnectAsync();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogAction?.Invoke(LogLevel.Error, null, null, ex);
|
||||
}
|
||||
|
||||
}
|
||||
private void DisConnect()
|
||||
{
|
||||
try
|
||||
{
|
||||
_serialSession.Close();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogAction?.Invoke(LogLevel.Error, null, null, ex);
|
||||
}
|
||||
}
|
||||
private void LogOut(LogLevel logLevel, object source, string message, Exception exception) => LogAction?.Invoke(logLevel, source, message, exception);
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
@*
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
*@
|
||||
|
||||
@namespace ThingsGateway.Foundation.Demo
|
||||
@using BlazorComponent;
|
||||
@using Microsoft.AspNetCore.Components.Web;
|
||||
@using System.IO.Ports;
|
||||
@using System.Collections.Concurrent;
|
||||
@using ThingsGateway.Foundation.Core;
|
||||
@using Masa.Blazor
|
||||
<MCard Flat Class="pa-2 my-1" Style="width:100%">
|
||||
<div class="mb-4">通道配置</div>
|
||||
<MRow Justify="JustifyTypes.Start" Align="AlignTypes.Center">
|
||||
|
||||
<MTextField Class="ma-1" Style="max-width:100px" Label="IP地址" Dense Outlined HideDetails="@("auto")" @bind-Value=@_ip />
|
||||
<MTextField Class="ma-1" Style="max-width:100px" Label="端口" Dense Outlined HideDetails="@("auto")" @bind-Value=@_port />
|
||||
|
||||
<MButton Class="ma-1" OnClick=@ConnectAsync Color="primary">
|
||||
连接
|
||||
</MButton>
|
||||
<MButton Class="ma-1" OnClick=@DisConnect Color="red">
|
||||
断开
|
||||
</MButton>
|
||||
</MRow>
|
||||
</MCard>
|
||||
@@ -0,0 +1,116 @@
|
||||
#region copyright
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
#endregion
|
||||
|
||||
namespace ThingsGateway.Foundation.Demo;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public partial class TcpClientPage : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// 日志输出
|
||||
/// </summary>
|
||||
public Action<LogLevel, object, string, Exception> LogAction;
|
||||
|
||||
private TouchSocketConfig _config;
|
||||
/// <summary>
|
||||
/// IP
|
||||
/// </summary>
|
||||
private string _ip = "127.0.0.1";
|
||||
/// <summary>
|
||||
/// Port
|
||||
/// </summary>
|
||||
public int _port { get; set; } = 502;
|
||||
|
||||
private TcpClient _tcpClient { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// <inheritdoc/>
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
_tcpClient.SafeDispose();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取对象
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public TcpClient GetTcpClient()
|
||||
{
|
||||
_config ??= new TouchSocketConfig();
|
||||
var LogMessage = new LoggerGroup() { LogLevel = LogLevel.Trace };
|
||||
LogMessage.AddLogger(new EasyLogger(LogOut) { LogLevel = LogLevel.Trace });
|
||||
_config.ConfigureContainer(a => a.RegisterSingleton<ILog>(LogMessage));
|
||||
_config.SetRemoteIPHost(new IPHost(_ip + ":" + _port));
|
||||
//载入配置
|
||||
_tcpClient.Setup(_config);
|
||||
return _tcpClient;
|
||||
}
|
||||
|
||||
internal void StateHasChangedAsync()
|
||||
{
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <inheritdoc/>
|
||||
/// </summary>
|
||||
/// <param name="firstRender"></param>
|
||||
protected override void OnAfterRender(bool firstRender)
|
||||
{
|
||||
if (firstRender)
|
||||
{
|
||||
var LogMessage = new LoggerGroup() { LogLevel = LogLevel.Trace };
|
||||
LogMessage.AddLogger(new EasyLogger(LogOut) { LogLevel = LogLevel.Trace });
|
||||
_config.ConfigureContainer(a => a.RegisterSingleton<ILog>(LogMessage));
|
||||
_config.SetRemoteIPHost(new IPHost(_ip + ":" + _port));
|
||||
_tcpClient.Setup(_config);
|
||||
}
|
||||
base.OnAfterRender(firstRender);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
_config ??= new TouchSocketConfig();
|
||||
|
||||
base.OnInitialized();
|
||||
}
|
||||
|
||||
private async Task ConnectAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
_tcpClient.Close();
|
||||
await GetTcpClient().ConnectAsync();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
LogAction?.Invoke(LogLevel.Error, null, null, ex);
|
||||
}
|
||||
|
||||
}
|
||||
private void DisConnect()
|
||||
{
|
||||
try
|
||||
{
|
||||
_tcpClient.Close();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
LogAction?.Invoke(LogLevel.Error, null, null, ex);
|
||||
}
|
||||
}
|
||||
private void LogOut(LogLevel logLevel, object source, string message, Exception exception) => LogAction?.Invoke(logLevel, source, message, exception);
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
@*
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
*@
|
||||
|
||||
@namespace ThingsGateway.Foundation.Demo
|
||||
@using BlazorComponent;
|
||||
@using Microsoft.AspNetCore.Components.Web;
|
||||
@using System.IO.Ports;
|
||||
@using System.Collections.Concurrent;
|
||||
@using ThingsGateway.Foundation.Core;
|
||||
@using Masa.Blazor
|
||||
|
||||
<MCard Flat Class="pa-2 my-1" Style="width:100%">
|
||||
<div class="mb-4">通道配置</div>
|
||||
<MRow Justify="JustifyTypes.Start" Align="AlignTypes.Center">
|
||||
|
||||
<MTextField Class="ma-1" Style="max-width:100px" Label="IP地址" Dense Outlined HideDetails="@("auto")" @bind-Value=@_ip />
|
||||
<MTextField Class="ma-1" Style="max-width:100px" Label="端口" Dense Outlined HideDetails="@("auto")" @bind-Value=@_port />
|
||||
|
||||
<MButton Class="ma-1" OnClick=@Connect Color="primary">
|
||||
启动
|
||||
</MButton>
|
||||
<MButton Class="ma-1" OnClick=@DisConnect Color="red">
|
||||
停止
|
||||
</MButton>
|
||||
|
||||
|
||||
|
||||
</MRow>
|
||||
</MCard>
|
||||
@@ -0,0 +1,111 @@
|
||||
#region copyright
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
#endregion
|
||||
|
||||
namespace ThingsGateway.Foundation.Demo;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public partial class TcpServerPage : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// 日志输出
|
||||
/// </summary>
|
||||
public Action<LogLevel, object, string, Exception> LogAction;
|
||||
|
||||
private TouchSocketConfig _config;
|
||||
|
||||
private string _ip = "127.0.0.1";
|
||||
|
||||
private int _port = 502;
|
||||
|
||||
private TcpService _tcpServer { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// <inheritdoc/>
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
_tcpServer.SafeDispose();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取对象
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public TcpService GetTcpServer()
|
||||
{
|
||||
_config ??= new TouchSocketConfig();
|
||||
var LogMessage = new LoggerGroup() { LogLevel = LogLevel.Trace };
|
||||
LogMessage.AddLogger(new EasyLogger(LogOut) { LogLevel = LogLevel.Trace });
|
||||
_config.ConfigureContainer(a => a.RegisterSingleton<ILog>(LogMessage));
|
||||
_config.SetListenIPHosts(new IPHost[] { new IPHost(_ip + ":" + _port) });
|
||||
//载入配置
|
||||
_tcpServer.Setup(_config);
|
||||
return _tcpServer;
|
||||
}
|
||||
|
||||
internal void StateHasChangedAsync()
|
||||
{
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <inheritdoc/>
|
||||
/// </summary>
|
||||
/// <param name="firstRender"></param>
|
||||
protected override void OnAfterRender(bool firstRender)
|
||||
{
|
||||
if (firstRender)
|
||||
{
|
||||
var LogMessage = new LoggerGroup() { LogLevel = LogLevel.Trace };
|
||||
LogMessage.AddLogger(new EasyLogger(LogOut) { LogLevel = LogLevel.Trace });
|
||||
_config.ConfigureContainer(a => a.RegisterSingleton<ILog>(LogMessage));
|
||||
_config.SetListenIPHosts(new IPHost[] { new IPHost(_ip + ":" + _port) });
|
||||
_tcpServer.Setup(_config);
|
||||
}
|
||||
base.OnAfterRender(firstRender);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
_config ??= new TouchSocketConfig();
|
||||
|
||||
base.OnInitialized();
|
||||
}
|
||||
|
||||
private void Connect()
|
||||
{
|
||||
try
|
||||
{
|
||||
_tcpServer.Stop();
|
||||
GetTcpServer().Start();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
LogAction?.Invoke(LogLevel.Error, null, null, ex);
|
||||
}
|
||||
|
||||
}
|
||||
private void DisConnect()
|
||||
{
|
||||
try
|
||||
{
|
||||
_tcpServer.Stop();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogAction?.Invoke(LogLevel.Error, null, null, ex);
|
||||
}
|
||||
}
|
||||
private void LogOut(LogLevel logLevel, object source, string message, Exception exception) => LogAction?.Invoke(logLevel, source, message, exception);
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
@*
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
*@
|
||||
|
||||
@namespace ThingsGateway.Foundation.Demo
|
||||
@using BlazorComponent;
|
||||
@using Microsoft.AspNetCore.Components.Web;
|
||||
@using System.IO.Ports;
|
||||
@using System.Collections.Concurrent;
|
||||
@using ThingsGateway.Foundation.Core;
|
||||
@using Masa.Blazor
|
||||
|
||||
<MCard Flat Class="pa-2 my-1" Style="width:100%">
|
||||
<div class="mb-4">通道配置</div>
|
||||
<MRow Justify="JustifyTypes.Start" Align="AlignTypes.Center">
|
||||
|
||||
<MTextField Class="ma-1" Style="max-width:100px" Label="IP地址" Dense Outlined HideDetails="@("auto")" @bind-Value=@_ip />
|
||||
<MTextField Class="ma-1" Style="max-width:100px" Label="端口" Dense Outlined HideDetails="@("auto")" @bind-Value=@_port />
|
||||
|
||||
<MButton Class="ma-1" OnClick=Connect Color="primary">
|
||||
连接
|
||||
</MButton>
|
||||
<MButton Class="ma-1" OnClick=DisConnect Color="red">
|
||||
断开
|
||||
</MButton>
|
||||
|
||||
|
||||
</MRow>
|
||||
</MCard>
|
||||
@@ -0,0 +1,118 @@
|
||||
#region copyright
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
#endregion
|
||||
|
||||
namespace ThingsGateway.Foundation.Demo;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public partial class UdpSessionPage : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// IP
|
||||
/// </summary>
|
||||
public string _ip = "127.0.0.1";
|
||||
|
||||
/// <summary>
|
||||
/// Port
|
||||
/// </summary>
|
||||
public int _port = 502;
|
||||
|
||||
/// <summary>
|
||||
/// 日志输出
|
||||
/// </summary>
|
||||
public Action<LogLevel, object, string, Exception> LogAction;
|
||||
|
||||
private TouchSocketConfig _config;
|
||||
private UdpSession _udpSession { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// <inheritdoc/>
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
_udpSession.SafeDispose();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取对象
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public UdpSession GetUdpSession()
|
||||
{
|
||||
_config ??= new TouchSocketConfig();
|
||||
var LogMessage = new LoggerGroup() { LogLevel = LogLevel.Trace };
|
||||
LogMessage.AddLogger(new EasyLogger(LogOut) { LogLevel = LogLevel.Trace });
|
||||
_config.ConfigureContainer(a => a.RegisterSingleton<ILog>(LogMessage));
|
||||
_config.SetRemoteIPHost(new IPHost(_ip + ":" + _port));
|
||||
_config.SetBindIPHost(new IPHost(0));
|
||||
//载入配置
|
||||
_udpSession.Setup(_config);
|
||||
return _udpSession;
|
||||
}
|
||||
|
||||
internal void StateHasChangedAsync()
|
||||
{
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <inheritdoc/>
|
||||
/// </summary>
|
||||
/// <param name="firstRender"></param>
|
||||
protected override void OnAfterRender(bool firstRender)
|
||||
{
|
||||
if (firstRender)
|
||||
{
|
||||
var LogMessage = new LoggerGroup() { LogLevel = LogLevel.Trace };
|
||||
LogMessage.AddLogger(new EasyLogger(LogOut) { LogLevel = LogLevel.Trace });
|
||||
_config.ConfigureContainer(a => a.RegisterSingleton<ILog>(LogMessage));
|
||||
_config.SetRemoteIPHost(new IPHost(_ip + ":" + _port));
|
||||
_config.SetBindIPHost(new IPHost(0));
|
||||
_udpSession.Setup(_config);
|
||||
}
|
||||
base.OnAfterRender(firstRender);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
_config ??= new TouchSocketConfig();
|
||||
|
||||
base.OnInitialized();
|
||||
}
|
||||
|
||||
private void Connect()
|
||||
{
|
||||
try
|
||||
{
|
||||
_udpSession.Stop();
|
||||
GetUdpSession().Start();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogAction?.Invoke(LogLevel.Error, null, null, ex);
|
||||
}
|
||||
|
||||
}
|
||||
private void DisConnect()
|
||||
{
|
||||
try
|
||||
{
|
||||
_udpSession.Stop();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
LogAction?.Invoke(LogLevel.Error, null, null, ex);
|
||||
}
|
||||
}
|
||||
private void LogOut(LogLevel logLevel, object source, string message, Exception exception) => LogAction?.Invoke(logLevel, source, message, exception);
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
#region copyright
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
#endregion
|
||||
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace ThingsGateway.Foundation.Demo;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public class DeviceVariableRunTime : IDeviceVariableRunTime
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
[Description("读取间隔")]
|
||||
public int? IntervalTime { get; set; }
|
||||
/// <inheritdoc/>
|
||||
[Description("变量地址")]
|
||||
public string Address { get; set; }
|
||||
/// <inheritdoc/>
|
||||
public int Index { get; set; }
|
||||
/// <inheritdoc/>
|
||||
public IThingsGatewayBitConverter ThingsGatewayBitConverter { get; set; }
|
||||
/// <inheritdoc/>
|
||||
[Description("数据类型")]
|
||||
public DataTypeEnum DataTypeEnum { get; set; }
|
||||
/// <inheritdoc/>
|
||||
[Description("实时值")]
|
||||
public object Value { get; set; }
|
||||
|
||||
public bool IsOnline { get; set; }
|
||||
|
||||
public string LastErrorMessage { get; set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public OperResult SetValue(object value, DateTime dateTime = default, string lastErrorMessage = null)
|
||||
{
|
||||
Value = value;
|
||||
return OperResult.CreateSuccessResult();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
#region copyright
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
#endregion
|
||||
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace ThingsGateway.Foundation.Demo;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public class DeviceVariableSourceRead : IDeviceVariableSourceRead<IDeviceVariableRunTime>
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public TimerTick IntervalTimeTick { get; set; }
|
||||
/// <inheritdoc/>
|
||||
public string Address { get; set; }
|
||||
/// <inheritdoc/>
|
||||
public int Length { get; set; }
|
||||
/// <inheritdoc/>
|
||||
public List<IDeviceVariableRunTime> DeviceVariableRunTimes { get; set; } = new List<IDeviceVariableRunTime>();
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
#region copyright
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
#endregion
|
||||
|
||||
global using System;
|
||||
global using System.Threading;
|
||||
global using System.Threading.Tasks;
|
||||
|
||||
global using ThingsGateway.Components;
|
||||
global using ThingsGateway.Foundation.Core;
|
||||
global using ThingsGateway.Foundation.Serial;
|
||||
global using ThingsGateway.Foundation.Sockets;
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
@*
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
*@
|
||||
|
||||
@page "/"
|
||||
@layout BaseLayout
|
||||
@inject NavigationManager NavigationManager
|
||||
@namespace ThingsGateway.Foundation.Demo
|
||||
@using Microsoft.AspNetCore.Authorization;
|
||||
|
||||
@code {
|
||||
|
||||
/// <summary>
|
||||
/// <inheritdoc/>
|
||||
/// </summary>
|
||||
/// <param name="firstRender"></param>
|
||||
/// <returns></returns>
|
||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||
{
|
||||
if (firstRender)
|
||||
{
|
||||
NavigationManager.NavigateTo("index");
|
||||
}
|
||||
|
||||
await base.OnAfterRenderAsync(firstRender);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,218 @@
|
||||
@*
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
*@
|
||||
|
||||
@page "/DLT645_2007"
|
||||
|
||||
@using BlazorComponent;
|
||||
@using Microsoft.AspNetCore.Components.Web;
|
||||
@using Microsoft.JSInterop;
|
||||
@using ThingsGateway.Foundation.Core;
|
||||
@using Masa.Blazor;
|
||||
@namespace ThingsGateway.Foundation.Demo
|
||||
|
||||
<SerialSessionPage @ref=_serialSessionPage></SerialSessionPage>
|
||||
|
||||
<MCard Flat Class="pa-2 my-1" Style="width:100%">
|
||||
<div class="mb-4">驱动配置</div>
|
||||
<MRow Justify="JustifyTypes.Start" Align="AlignTypes.Center">
|
||||
@if (_plc != null)
|
||||
{
|
||||
|
||||
<MTextField Class="ma-1" Outlined Style="max-width:100px" Label=@(_plc.Description(x => x.FrameTime)) Dense HideDetails="@("auto")" @bind-Value=@_plc.FrameTime></MTextField>
|
||||
<MTextField Class="ma-1" Outlined Style="max-width:100px" Label=@(_plc.Description(x => x.CacheTimeout)) Dense HideDetails="@("auto")" @bind-Value=@_plc.CacheTimeout></MTextField>
|
||||
|
||||
|
||||
|
||||
<MTextField Class="ma-1" Outlined Style="max-width:100px" Label=@(_plc.Description(x => x.Station)) Dense HideDetails="@("auto")" @bind-Value=@_plc.Station></MTextField>
|
||||
<MTextField Class="ma-1" Outlined Style="max-width:100px" Label=@(_plc.Description(x => x.TimeOut)) Dense HideDetails="@("auto")" @bind-Value=@_plc.TimeOut></MTextField>
|
||||
|
||||
<MTextField Class="ma-1" Outlined Style="max-width:100px" Label=@(_plc.Description(x => x.OperCode)) Dense HideDetails="@("auto")" @bind-Value=@_plc.OperCode></MTextField>
|
||||
<MTextField Class="ma-1" Outlined Style="max-width:100px" Label=@(_plc.Description(x => x.Password)) Dense HideDetails="@("auto")" @bind-Value=@_plc.Password></MTextField>
|
||||
|
||||
|
||||
<MSelect Class="ma-1" Outlined Style="max-width:200px" @bind-Value="_plc.DataFormat" Label="@(_plc.Description(x => x.DataFormat))"
|
||||
Items=@(typeof(DataFormat).GetEnumList())
|
||||
MenuProps="@(props => { props.Auto = true; props.OffsetY = true; })"
|
||||
ItemText=@((u) =>u.Description)
|
||||
ItemValue=@(u =>(DataFormat)u.Value)
|
||||
HideDetails=@("auto") Height="30"
|
||||
Dense>
|
||||
</MSelect>
|
||||
|
||||
<MCheckbox Class="ma-1" Style="max-width:200px" Label=@(_plc.Description(x => x.EnableFEHead)) Dense HideDetails="@("auto")" @bind-Value=@_plc.EnableFEHead></MCheckbox>
|
||||
|
||||
}
|
||||
</MRow>
|
||||
</MCard>
|
||||
|
||||
|
||||
<DriverDebugUIPage @ref=_driverDebugUIPage Sections="_sections" ShowDefaultOtherContent=false>
|
||||
<OtherContent>
|
||||
<MSubheader>
|
||||
广播校时
|
||||
</MSubheader>
|
||||
<MContainer>
|
||||
<MRow Dense Align="AlignTypes.Center">
|
||||
|
||||
<PDateDigitalClockPicker @bind-Value="dateTime" TimeFormat="TimeFormat.Hr24" ViewType="DateTimePickerViewType.Auto" Context="dateContext">
|
||||
<ActivatorContent>
|
||||
<MTextField @bind-Value="dateTime" Style="max-width:200px;" Class="ma-1" Outlined HideDetails=@("auto") Dense Clearable Readonly @attributes="@dateContext.Attrs">
|
||||
</MTextField>
|
||||
</ActivatorContent>
|
||||
</PDateDigitalClockPicker>
|
||||
<MButton Class="ma-1" Color="primary" OnClick="()=>_plc.BroadcastTime(dateTime)">
|
||||
校时
|
||||
</MButton>
|
||||
</MRow>
|
||||
</MContainer>
|
||||
|
||||
<MSubheader>
|
||||
冻结
|
||||
</MSubheader>
|
||||
<MContainer>
|
||||
<MRow Dense Align="AlignTypes.Center">
|
||||
|
||||
<PDateDigitalClockPicker @bind-Value="dateTime" TimeFormat="TimeFormat.Hr24" ViewType="DateTimePickerViewType.Auto" Context="dateContext">
|
||||
<ActivatorContent>
|
||||
<MTextField @bind-Value="dateTime" Style="max-width:200px;" Class="ma-1" Outlined HideDetails=@("auto") Dense Clearable Readonly @attributes="@dateContext.Attrs">
|
||||
</MTextField>
|
||||
</ActivatorContent>
|
||||
</PDateDigitalClockPicker>
|
||||
<MButton Class="ma-1" Color="primary" OnClick="()=>_plc.FreezeAsync(dateTime)">
|
||||
冻结
|
||||
</MButton>
|
||||
</MRow>
|
||||
</MContainer>
|
||||
<MSubheader>
|
||||
读取通信地址
|
||||
</MSubheader>
|
||||
<MContainer>
|
||||
<MRow Dense Align="AlignTypes.Center">
|
||||
|
||||
<MButton Class="ma-1" Color="primary" OnClick="ReadDeviceStationAsync">
|
||||
读取通信地址
|
||||
</MButton>
|
||||
</MRow>
|
||||
</MContainer>
|
||||
<MSubheader>
|
||||
修改波特率
|
||||
</MSubheader>
|
||||
<MContainer>
|
||||
<MRow Dense Align="AlignTypes.Center">
|
||||
|
||||
<MTextField Class="ma-1" Outlined Style="max-width:200px" Label=波特率 Clearable Dense HideDetails="@("auto")" @bind-Value=baudRate></MTextField>
|
||||
|
||||
<MButton Class="ma-1" Color="primary" OnClick="WriteBaudRateAsync">
|
||||
修改波特率
|
||||
</MButton>
|
||||
</MRow>
|
||||
</MContainer>
|
||||
|
||||
|
||||
<MSubheader>
|
||||
更新通信地址
|
||||
</MSubheader>
|
||||
<MContainer>
|
||||
<MRow Dense Align="AlignTypes.Center">
|
||||
|
||||
<MTextField Class="ma-1" Outlined Style="max-width:200px" Label=通信地址 Clearable Dense HideDetails="@("auto")" @bind-Value=station></MTextField>
|
||||
|
||||
<MButton Class="ma-1" Color="primary" OnClick="WriteDeviceStationAsync">
|
||||
更新通信地址
|
||||
</MButton>
|
||||
</MRow>
|
||||
</MContainer>
|
||||
|
||||
<MSubheader>
|
||||
修改密码
|
||||
</MSubheader>
|
||||
<MContainer>
|
||||
<MRow Dense Align="AlignTypes.Center">
|
||||
|
||||
<MTextField Class="ma-1" Outlined Style="max-width:200px" Label=级别(0-9) Clearable Dense HideDetails="@("auto")" @bind-Value=level></MTextField>
|
||||
<MTextField Class="ma-1" Outlined Style="max-width:200px" Label=旧密码 Clearable Dense HideDetails="@("auto")" @bind-Value=oldPassword></MTextField>
|
||||
<MTextField Class="ma-1" Outlined Style="max-width:200px" Label=新密码 Clearable Dense HideDetails="@("auto")" @bind-Value=newPassword></MTextField>
|
||||
|
||||
<MButton Class="ma-1" Color="primary" OnClick="WritePasswordAsync">
|
||||
修改密码
|
||||
</MButton>
|
||||
</MRow>
|
||||
</MContainer>
|
||||
|
||||
|
||||
|
||||
</OtherContent>
|
||||
</DriverDebugUIPage>
|
||||
|
||||
@code {
|
||||
private byte level;
|
||||
private string oldPassword;
|
||||
private string newPassword;
|
||||
private async Task WritePasswordAsync()
|
||||
{
|
||||
var result = await _plc.WritePasswordAsync(level, oldPassword, newPassword);
|
||||
if (result.IsSuccess)
|
||||
{
|
||||
_driverDebugUIPage.Messages.Add((Microsoft.Extensions.Logging.LogLevel.Information, DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(_driverDebugUIPage.InitTimezone.TimezoneOffset) + " - " + result.Message));
|
||||
}
|
||||
else
|
||||
{
|
||||
_driverDebugUIPage.Messages.Add((Microsoft.Extensions.Logging.LogLevel.Warning, DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(_driverDebugUIPage.InitTimezone.TimezoneOffset) + " - " + result.Message));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private string station;
|
||||
private async Task WriteDeviceStationAsync()
|
||||
{
|
||||
var result = await _plc.WriteDeviceStationAsync(station);
|
||||
if (result.IsSuccess)
|
||||
{
|
||||
_driverDebugUIPage.Messages.Add((Microsoft.Extensions.Logging.LogLevel.Information, DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(_driverDebugUIPage.InitTimezone.TimezoneOffset) + " - " + result.Message));
|
||||
}
|
||||
else
|
||||
{
|
||||
_driverDebugUIPage.Messages.Add((Microsoft.Extensions.Logging.LogLevel.Warning, DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(_driverDebugUIPage.InitTimezone.TimezoneOffset) + " - " + result.Message));
|
||||
}
|
||||
}
|
||||
|
||||
private int baudRate;
|
||||
private async Task WriteBaudRateAsync()
|
||||
{
|
||||
var result = await _plc.WriteBaudRateAsync(baudRate);
|
||||
if (result.IsSuccess)
|
||||
{
|
||||
_driverDebugUIPage.Messages.Add((Microsoft.Extensions.Logging.LogLevel.Information, DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(_driverDebugUIPage.InitTimezone.TimezoneOffset) + " - " + result.Message));
|
||||
}
|
||||
else
|
||||
{
|
||||
_driverDebugUIPage.Messages.Add((Microsoft.Extensions.Logging.LogLevel.Warning, DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(_driverDebugUIPage.InitTimezone.TimezoneOffset) + " - " + result.Message));
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ReadDeviceStationAsync()
|
||||
{
|
||||
var result = await _plc.ReadDeviceStationAsync();
|
||||
if (result.IsSuccess)
|
||||
{
|
||||
_driverDebugUIPage.Messages.Add((Microsoft.Extensions.Logging.LogLevel.Information, DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(_driverDebugUIPage.InitTimezone.TimezoneOffset) + " - " + result.Content));
|
||||
}
|
||||
else
|
||||
{
|
||||
_driverDebugUIPage.Messages.Add((Microsoft.Extensions.Logging.LogLevel.Warning, DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(_driverDebugUIPage.InitTimezone.TimezoneOffset) + " - " + result.Message));
|
||||
}
|
||||
}
|
||||
private DateTime dateTime;
|
||||
}
|
||||
@code {
|
||||
private readonly List<(string Code, string Language)> _sections = new();
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
#region copyright
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
#endregion
|
||||
|
||||
namespace ThingsGateway.Foundation.Demo;
|
||||
|
||||
|
||||
/// <inheritdoc/>
|
||||
public partial class DLT645_2007DebugPage
|
||||
{
|
||||
/// <summary>
|
||||
/// SerialSessionPage
|
||||
/// </summary>
|
||||
private SerialSessionPage _serialSessionPage;
|
||||
private DriverDebugUIPage _driverDebugUIPage;
|
||||
private ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007 _plc;
|
||||
/// <summary>
|
||||
/// <inheritdoc/>
|
||||
/// </summary>
|
||||
/// <param name="firstRender"></param>
|
||||
protected override void OnAfterRender(bool firstRender)
|
||||
{
|
||||
if (firstRender)
|
||||
{
|
||||
_sections.Add((
|
||||
"""
|
||||
private static async Task ModbusClientAsync()
|
||||
{
|
||||
//链路基础配置项
|
||||
var config = new TouchSocketConfig();
|
||||
config
|
||||
.SetSerialProperty(new SerialProperty() //串口链路才需要
|
||||
{
|
||||
PortName = "COM1"
|
||||
});
|
||||
var serialSession = new SerialSession();//链路对象
|
||||
serialSession.Setup(config);
|
||||
|
||||
//创建协议对象,构造函数需要传入对应链路对象
|
||||
DLT645_2007 plc = new(serialSession)//传入链路
|
||||
{
|
||||
//协议配置
|
||||
DataFormat = DataFormat.ABCD,
|
||||
FrameTime = 0,
|
||||
CacheTimeout = 1000,
|
||||
ConnectTimeOut = 3000,
|
||||
Station = 1,
|
||||
TimeOut = 3000,
|
||||
Crc16CheckEnable = true
|
||||
};
|
||||
|
||||
#region 读写测试
|
||||
|
||||
|
||||
var result = await plc.ReadStringAsync("00000000", 1);
|
||||
|
||||
#endregion
|
||||
|
||||
}
|
||||
|
||||
""", "csharp"));
|
||||
|
||||
|
||||
if (_serialSessionPage != null)
|
||||
_serialSessionPage.LogAction = _driverDebugUIPage.LogOut;
|
||||
_plc = new ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007(_serialSessionPage.GetSerialSession());
|
||||
_driverDebugUIPage.Plc = _plc;
|
||||
|
||||
//初始化
|
||||
_driverDebugUIPage.Address = "02010100";
|
||||
_driverDebugUIPage.DeviceVariableRunTimes.ForEach(a => a.Address = "02010100");
|
||||
|
||||
//载入配置
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
base.OnAfterRender(firstRender);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,217 @@
|
||||
@*
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
*@
|
||||
|
||||
@page "/DLT645_2007OverTcp"
|
||||
|
||||
@using BlazorComponent;
|
||||
@using Microsoft.AspNetCore.Components.Web;
|
||||
@using Microsoft.JSInterop;
|
||||
@using Masa.Blazor;
|
||||
@namespace ThingsGateway.Foundation.Demo
|
||||
|
||||
<TcpClientPage @ref=_tcpClientPage></TcpClientPage>
|
||||
|
||||
<MCard Flat Class="pa-2 my-1" Style="width:100%">
|
||||
<div class="mb-4">驱动配置</div>
|
||||
<MRow Justify="JustifyTypes.Start" Align="AlignTypes.Center">
|
||||
@if (_plc != null)
|
||||
{
|
||||
|
||||
<MTextField Class="ma-1" Outlined Style="max-width:100px" Label=@(_plc.Description(x => x.FrameTime)) Dense HideDetails="@("auto")" @bind-Value=@_plc.FrameTime></MTextField>
|
||||
<MTextField Class="ma-1" Outlined Style="max-width:100px" Label=@(_plc.Description(x => x.CacheTimeout)) Dense HideDetails="@("auto")" @bind-Value=@_plc.CacheTimeout></MTextField>
|
||||
|
||||
<MTextField Class="ma-1" Outlined Style="max-width:100px" Label=@(_plc.Description(x => x.Station)) Dense HideDetails="@("auto")" @bind-Value=@_plc.Station></MTextField>
|
||||
<MTextField Class="ma-1" Outlined Style="max-width:100px" Label=@(_plc.Description(x => x.TimeOut)) Dense HideDetails="@("auto")" @bind-Value=@_plc.TimeOut></MTextField>
|
||||
|
||||
<MTextField Class="ma-1" Outlined Style="max-width:100px" Label=@(_plc.Description(x => x.OperCode)) Dense HideDetails="@("auto")" @bind-Value=@_plc.OperCode></MTextField>
|
||||
<MTextField Class="ma-1" Outlined Style="max-width:100px" Label=@(_plc.Description(x => x.Password)) Dense HideDetails="@("auto")" @bind-Value=@_plc.Password></MTextField>
|
||||
|
||||
|
||||
<MSelect Class="ma-1" Outlined Style="max-width:200px" @bind-Value="_plc.DataFormat" Label="@(_plc.Description(x => x.DataFormat))"
|
||||
Items=@(typeof(DataFormat).GetEnumList())
|
||||
MenuProps="@(props => { props.Auto = true; props.OffsetY = true; })"
|
||||
ItemText=@((u) =>u.Description)
|
||||
ItemValue=@(u =>(DataFormat)u.Value)
|
||||
HideDetails=@("auto") Height="30"
|
||||
Dense>
|
||||
</MSelect>
|
||||
|
||||
<MCheckbox Class="ma-1" Style="max-width:200px" Label=@(_plc.Description(x => x.EnableFEHead)) Dense HideDetails="@("auto")" @bind-Value=@_plc.EnableFEHead></MCheckbox>
|
||||
|
||||
}
|
||||
</MRow>
|
||||
</MCard>
|
||||
|
||||
|
||||
<DriverDebugUIPage @ref=_driverDebugUIPage Sections="_sections" ShowDefaultOtherContent=false>
|
||||
<OtherContent>
|
||||
<MSubheader>
|
||||
广播校时
|
||||
</MSubheader>
|
||||
<MContainer>
|
||||
<MRow Dense Align="AlignTypes.Center">
|
||||
|
||||
<PDateDigitalClockPicker @bind-Value="dateTime" TimeFormat="TimeFormat.Hr24" ViewType="DateTimePickerViewType.Auto" Context="dateContext">
|
||||
<ActivatorContent>
|
||||
<MTextField @bind-Value="dateTime" Style="max-width:200px;" Class="ma-1" Outlined HideDetails=@("auto") Dense Clearable Readonly @attributes="@dateContext.Attrs">
|
||||
</MTextField>
|
||||
</ActivatorContent>
|
||||
</PDateDigitalClockPicker>
|
||||
<MButton Class="ma-1" Color="primary" OnClick="()=>_plc.BroadcastTime(dateTime)">
|
||||
校时
|
||||
</MButton>
|
||||
</MRow>
|
||||
</MContainer>
|
||||
|
||||
<MSubheader>
|
||||
冻结
|
||||
</MSubheader>
|
||||
<MContainer>
|
||||
<MRow Dense Align="AlignTypes.Center">
|
||||
|
||||
<PDateDigitalClockPicker @bind-Value="dateTime" TimeFormat="TimeFormat.Hr24" ViewType="DateTimePickerViewType.Auto" Context="dateContext">
|
||||
<ActivatorContent>
|
||||
<MTextField @bind-Value="dateTime" Style="max-width:200px;" Class="ma-1" Outlined HideDetails=@("auto") Dense Clearable Readonly @attributes="@dateContext.Attrs">
|
||||
</MTextField>
|
||||
</ActivatorContent>
|
||||
</PDateDigitalClockPicker>
|
||||
<MButton Class="ma-1" Color="primary" OnClick="()=>_plc.FreezeAsync(dateTime)">
|
||||
冻结
|
||||
</MButton>
|
||||
</MRow>
|
||||
</MContainer>
|
||||
<MSubheader>
|
||||
读取通信地址
|
||||
</MSubheader>
|
||||
<MContainer>
|
||||
<MRow Dense Align="AlignTypes.Center">
|
||||
|
||||
<MButton Class="ma-1" Color="primary" OnClick="ReadDeviceStationAsync">
|
||||
读取通信地址
|
||||
</MButton>
|
||||
</MRow>
|
||||
</MContainer>
|
||||
<MSubheader>
|
||||
修改波特率
|
||||
</MSubheader>
|
||||
<MContainer>
|
||||
<MRow Dense Align="AlignTypes.Center">
|
||||
|
||||
<MTextField Class="ma-1" Outlined Style="max-width:200px" Label=波特率 Clearable Dense HideDetails="@("auto")" @bind-Value=baudRate></MTextField>
|
||||
|
||||
<MButton Class="ma-1" Color="primary" OnClick="WriteBaudRateAsync">
|
||||
修改波特率
|
||||
</MButton>
|
||||
</MRow>
|
||||
</MContainer>
|
||||
|
||||
|
||||
<MSubheader>
|
||||
更新通信地址
|
||||
</MSubheader>
|
||||
<MContainer>
|
||||
<MRow Dense Align="AlignTypes.Center">
|
||||
|
||||
<MTextField Class="ma-1" Outlined Style="max-width:200px" Label=通信地址 Clearable Dense HideDetails="@("auto")" @bind-Value=station></MTextField>
|
||||
|
||||
<MButton Class="ma-1" Color="primary" OnClick="WriteDeviceStationAsync">
|
||||
更新通信地址
|
||||
</MButton>
|
||||
</MRow>
|
||||
</MContainer>
|
||||
|
||||
<MSubheader>
|
||||
修改密码
|
||||
</MSubheader>
|
||||
<MContainer>
|
||||
<MRow Dense Align="AlignTypes.Center">
|
||||
|
||||
<MTextField Class="ma-1" Outlined Style="max-width:200px" Label=级别(0-9) Clearable Dense HideDetails="@("auto")" @bind-Value=level></MTextField>
|
||||
<MTextField Class="ma-1" Outlined Style="max-width:200px" Label=旧密码 Clearable Dense HideDetails="@("auto")" @bind-Value=oldPassword></MTextField>
|
||||
<MTextField Class="ma-1" Outlined Style="max-width:200px" Label=新密码 Clearable Dense HideDetails="@("auto")" @bind-Value=newPassword></MTextField>
|
||||
|
||||
<MButton Class="ma-1" Color="primary" OnClick="WritePasswordAsync">
|
||||
修改密码
|
||||
</MButton>
|
||||
</MRow>
|
||||
</MContainer>
|
||||
|
||||
|
||||
|
||||
</OtherContent>
|
||||
</DriverDebugUIPage>
|
||||
|
||||
@code {
|
||||
private byte level;
|
||||
private string oldPassword;
|
||||
private string newPassword;
|
||||
private async Task WritePasswordAsync()
|
||||
{
|
||||
var result = await _plc.WritePasswordAsync(level, oldPassword, newPassword);
|
||||
if (result.IsSuccess)
|
||||
{
|
||||
_driverDebugUIPage.Messages.Add((Microsoft.Extensions.Logging.LogLevel.Information, DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(_driverDebugUIPage.InitTimezone.TimezoneOffset) + " - " + result.Message));
|
||||
}
|
||||
else
|
||||
{
|
||||
_driverDebugUIPage.Messages.Add((Microsoft.Extensions.Logging.LogLevel.Warning, DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(_driverDebugUIPage.InitTimezone.TimezoneOffset) + " - " + result.Message));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private string station;
|
||||
private async Task WriteDeviceStationAsync()
|
||||
{
|
||||
var result = await _plc.WriteDeviceStationAsync(station);
|
||||
if (result.IsSuccess)
|
||||
{
|
||||
_driverDebugUIPage.Messages.Add((Microsoft.Extensions.Logging.LogLevel.Information, DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(_driverDebugUIPage.InitTimezone.TimezoneOffset) + " - " + result.Message));
|
||||
}
|
||||
else
|
||||
{
|
||||
_driverDebugUIPage.Messages.Add((Microsoft.Extensions.Logging.LogLevel.Warning, DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(_driverDebugUIPage.InitTimezone.TimezoneOffset) + " - " + result.Message));
|
||||
}
|
||||
}
|
||||
|
||||
private int baudRate;
|
||||
private async Task WriteBaudRateAsync()
|
||||
{
|
||||
var result = await _plc.WriteBaudRateAsync(baudRate);
|
||||
if (result.IsSuccess)
|
||||
{
|
||||
_driverDebugUIPage.Messages.Add((Microsoft.Extensions.Logging.LogLevel.Information, DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(_driverDebugUIPage.InitTimezone.TimezoneOffset) + " - " + result.Message));
|
||||
}
|
||||
else
|
||||
{
|
||||
_driverDebugUIPage.Messages.Add((Microsoft.Extensions.Logging.LogLevel.Warning, DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(_driverDebugUIPage.InitTimezone.TimezoneOffset) + " - " + result.Message));
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ReadDeviceStationAsync()
|
||||
{
|
||||
var result = await _plc.ReadDeviceStationAsync();
|
||||
if (result.IsSuccess)
|
||||
{
|
||||
_driverDebugUIPage.Messages.Add((Microsoft.Extensions.Logging.LogLevel.Information, DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(_driverDebugUIPage.InitTimezone.TimezoneOffset) + " - " + result.Content));
|
||||
}
|
||||
else
|
||||
{
|
||||
_driverDebugUIPage.Messages.Add((Microsoft.Extensions.Logging.LogLevel.Warning, DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(_driverDebugUIPage.InitTimezone.TimezoneOffset) + " - " + result.Message));
|
||||
}
|
||||
}
|
||||
private DateTime dateTime;
|
||||
}
|
||||
|
||||
@code {
|
||||
private readonly List<(string Code, string Language)> _sections = new();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
#region copyright
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
#endregion
|
||||
|
||||
namespace ThingsGateway.Foundation.Demo;
|
||||
|
||||
|
||||
/// <inheritdoc/>
|
||||
public partial class DLT645_2007OverTcpDebugPage
|
||||
{
|
||||
/// <summary>
|
||||
/// SerialSessionPage
|
||||
/// </summary>
|
||||
private TcpClientPage _tcpClientPage;
|
||||
private DriverDebugUIPage _driverDebugUIPage;
|
||||
|
||||
private ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007OverTcp _plc;
|
||||
|
||||
/// <summary>
|
||||
/// <inheritdoc/>
|
||||
/// </summary>
|
||||
/// <param name="firstRender"></param>
|
||||
protected override void OnAfterRender(bool firstRender)
|
||||
{
|
||||
if (firstRender)
|
||||
{
|
||||
_sections.Add((
|
||||
"""
|
||||
private static async Task ModbusClientAsync()
|
||||
{
|
||||
//链路基础配置项
|
||||
var config = new TouchSocketConfig();
|
||||
config
|
||||
.SetRemoteIPHost(new IPHost("127.0.0.1:502"))//TCP/UDP链路才需要
|
||||
|
||||
var tcpClient1 = new TcpClient();//链路对象
|
||||
tcpClient1.Setup(config);
|
||||
|
||||
//创建协议对象,构造函数需要传入对应链路对象
|
||||
DLT645_2007OverTcp plc = new(tcpClient1)
|
||||
{
|
||||
//协议配置
|
||||
DataFormat = DataFormat.ABCD,
|
||||
FrameTime = 0,
|
||||
CacheTimeout = 1000,
|
||||
ConnectTimeOut = 3000,
|
||||
Station = 1,
|
||||
TimeOut = 3000,
|
||||
Crc16CheckEnable = true
|
||||
};
|
||||
|
||||
#region 读写测试
|
||||
var result = await plc.ReadStringAsync("00000000", 1);
|
||||
|
||||
#endregion
|
||||
|
||||
}
|
||||
|
||||
""", "csharp"));
|
||||
|
||||
if (_tcpClientPage != null)
|
||||
_tcpClientPage.LogAction = _driverDebugUIPage.LogOut;
|
||||
_plc = new ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007OverTcp(_tcpClientPage.GetTcpClient());
|
||||
_driverDebugUIPage.Plc = _plc;
|
||||
|
||||
//初始化
|
||||
_driverDebugUIPage.Address = "02010100";
|
||||
_driverDebugUIPage.DeviceVariableRunTimes.ForEach(a => a.Address = "02010100");
|
||||
_tcpClientPage._port = 5000;
|
||||
_tcpClientPage.StateHasChangedAsync();
|
||||
|
||||
//载入配置
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
base.OnAfterRender(firstRender);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
@*
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
*@
|
||||
|
||||
@page "/index"
|
||||
|
||||
<div class="ml-2">
|
||||
<div class="my-6 ">
|
||||
<MLabel Class="text-h3" Color="primary">ThingsGateway</MLabel>
|
||||
</div>
|
||||
<div>
|
||||
<strong class="text--lighten-1 text-h5 my-1">文档</strong>
|
||||
</div>
|
||||
<div class="my-2 ml-4">
|
||||
<PCopyableText>
|
||||
https://diego2098.gitee.io/thingsgateway-docs/
|
||||
</PCopyableText>
|
||||
</div>
|
||||
<div>
|
||||
<strong class="text--lighten-1 text-h5 my-1">协议</strong>
|
||||
</div>
|
||||
<div class="my-2 ml-4">
|
||||
<PCopyableText Text="https://gitee.com/diego2098/ThingsGateway/blob/master/LICENSE.zh">
|
||||
Apache-2.0开源协议
|
||||
</PCopyableText>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<strong class="text--lighten-1 text-h5 my-1">赞助</strong>
|
||||
</div>
|
||||
<div class="my-2 ml-4">
|
||||
<PCopyableText Text="https://diego2098.gitee.io/thingsgateway-docs/docs/donate">
|
||||
ThingsGateway赞助途径
|
||||
</PCopyableText>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<strong class="text--lighten-1 text-h5 my-1">社区</strong>
|
||||
</div>
|
||||
<div class="my-2 ml-4">
|
||||
<PCopyableText Text="605534569">
|
||||
QQ群:605534569
|
||||
</PCopyableText>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
@*
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
*@
|
||||
|
||||
@page "/ModbusRtu"
|
||||
|
||||
@using BlazorComponent;
|
||||
@using Microsoft.AspNetCore.Components.Web;
|
||||
@using Microsoft.JSInterop;
|
||||
@using ThingsGateway.Foundation.Core;
|
||||
@using Masa.Blazor;
|
||||
@namespace ThingsGateway.Foundation.Demo
|
||||
|
||||
<SerialSessionPage @ref=_serialSessionPage></SerialSessionPage>
|
||||
|
||||
<MCard Flat Class="pa-2 my-1" Style="width:100%">
|
||||
<div class="mb-4">驱动配置</div>
|
||||
<MRow Justify="JustifyTypes.Start" Align="AlignTypes.Center">
|
||||
@if (_plc != null)
|
||||
{
|
||||
|
||||
<MTextField Class="ma-1" Outlined Style="max-width:100px" Label=@(_plc.Description(x => x.FrameTime)) Dense HideDetails="@("auto")" @bind-Value=@_plc.FrameTime></MTextField>
|
||||
<MTextField Class="ma-1" Outlined Style="max-width:100px" Label=@(_plc.Description(x => x.CacheTimeout)) Dense HideDetails="@("auto")" @bind-Value=@_plc.CacheTimeout></MTextField>
|
||||
|
||||
<MTextField Class="ma-1" Outlined Style="max-width:100px" Label=@(_plc.Description(x => x.Station)) Dense HideDetails="@("auto")" @bind-Value=@_plc.Station></MTextField>
|
||||
<MTextField Class="ma-1" Outlined Style="max-width:100px" Label=@(_plc.Description(x => x.TimeOut)) Dense HideDetails="@("auto")" @bind-Value=@_plc.TimeOut></MTextField>
|
||||
|
||||
<MSelect Class="ma-1" Outlined Style="max-width:200px" @bind-Value="_plc.DataFormat" Label="@(_plc.Description(x => x.DataFormat))"
|
||||
Items=@(typeof(DataFormat).GetEnumList())
|
||||
MenuProps="@(props => { props.Auto = true; props.OffsetY = true; })"
|
||||
ItemText=@((u) =>u.Description)
|
||||
ItemValue=@(u =>(DataFormat)u.Value)
|
||||
HideDetails=@("auto") Height="30"
|
||||
Dense>
|
||||
</MSelect>
|
||||
|
||||
<MCheckbox Class="ma-1" Style="max-width:200px" Label=@(_plc.Description(x => x.Crc16CheckEnable)) Dense HideDetails="@("auto")" @bind-Value=@_plc.Crc16CheckEnable></MCheckbox>
|
||||
|
||||
}
|
||||
</MRow>
|
||||
</MCard>
|
||||
|
||||
|
||||
<DriverDebugUIPage @ref=_driverDebugUIPage Sections="_sections">
|
||||
|
||||
</DriverDebugUIPage>
|
||||
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
#region copyright
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
#endregion
|
||||
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace ThingsGateway.Foundation.Demo;
|
||||
|
||||
|
||||
/// <inheritdoc/>
|
||||
public partial class ModbusRtuDebugPage
|
||||
{
|
||||
/// <summary>
|
||||
/// SerialSessionPage
|
||||
/// </summary>
|
||||
private SerialSessionPage _serialSessionPage;
|
||||
private readonly List<(string Code, string Language)> _sections = new();
|
||||
private DriverDebugUIPage _driverDebugUIPage;
|
||||
|
||||
private ThingsGateway.Foundation.Adapter.Modbus.ModbusRtu _plc;
|
||||
/// <summary>
|
||||
/// <inheritdoc/>
|
||||
/// </summary>
|
||||
/// <param name="firstRender"></param>
|
||||
protected override void OnAfterRender(bool firstRender)
|
||||
{
|
||||
if (firstRender)
|
||||
{
|
||||
_sections.Add((
|
||||
"""
|
||||
private static async Task ModbusClientAsync()
|
||||
{
|
||||
//链路基础配置项
|
||||
var config = new TouchSocketConfig();
|
||||
config
|
||||
.SetSerialProperty(new SerialProperty() //串口链路才需要
|
||||
{
|
||||
PortName = "COM1"
|
||||
});
|
||||
var serialSession = new SerialSession();//链路对象
|
||||
serialSession.Setup(config);
|
||||
|
||||
//创建协议对象,构造函数需要传入对应链路对象
|
||||
ModbusRtu plc = new(serialSession)//传入链路
|
||||
{
|
||||
//协议配置
|
||||
DataFormat = DataFormat.ABCD,
|
||||
FrameTime = 0,
|
||||
CacheTimeout = 1000,
|
||||
ConnectTimeOut = 3000,
|
||||
Station = 1,
|
||||
TimeOut = 3000,
|
||||
Crc16CheckEnable = true
|
||||
};
|
||||
|
||||
#region 读写测试
|
||||
var bytesResult = await plc.ReadAsync("400001", 20);
|
||||
var int32sResult = await plc.ReadInt32Async("400001", 20);
|
||||
|
||||
#endregion
|
||||
|
||||
}
|
||||
|
||||
""", "csharp"));
|
||||
|
||||
|
||||
if (_serialSessionPage != null)
|
||||
_serialSessionPage.LogAction = _driverDebugUIPage.LogOut;
|
||||
_plc = new ThingsGateway.Foundation.Adapter.Modbus.ModbusRtu(_serialSessionPage.GetSerialSession());
|
||||
_driverDebugUIPage.Plc = _plc;
|
||||
//载入配置
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
base.OnAfterRender(firstRender);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
@*
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
*@
|
||||
|
||||
@page "/ModbusRtuOverTcp"
|
||||
|
||||
@using BlazorComponent;
|
||||
@using Microsoft.AspNetCore.Components.Web;
|
||||
@using Microsoft.JSInterop;
|
||||
@using Masa.Blazor;
|
||||
@namespace ThingsGateway.Foundation.Demo
|
||||
|
||||
<TcpClientPage @ref=_tcpClientPage></TcpClientPage>
|
||||
|
||||
<MCard Flat Class="pa-2 my-1" Style="width:100%">
|
||||
<div class="mb-4">驱动配置</div>
|
||||
<MRow Justify="JustifyTypes.Start" Align="AlignTypes.Center">
|
||||
@if (_plc != null)
|
||||
{
|
||||
|
||||
<MTextField Class="ma-1" Outlined Style="max-width:100px" Label=@(_plc.Description(x => x.FrameTime)) Dense HideDetails="@("auto")" @bind-Value=@_plc.FrameTime></MTextField>
|
||||
<MTextField Class="ma-1" Outlined Style="max-width:100px" Label=@(_plc.Description(x => x.CacheTimeout)) Dense HideDetails="@("auto")" @bind-Value=@_plc.CacheTimeout></MTextField>
|
||||
|
||||
<MTextField Class="ma-1" Outlined Style="max-width:100px" Label=@(_plc.Description(x => x.Station)) Dense HideDetails="@("auto")" @bind-Value=@_plc.Station></MTextField>
|
||||
<MTextField Class="ma-1" Outlined Style="max-width:100px" Label=@(_plc.Description(x => x.TimeOut)) Dense HideDetails="@("auto")" @bind-Value=@_plc.TimeOut></MTextField>
|
||||
|
||||
<MSelect Class="ma-1" Outlined Style="max-width:200px" @bind-Value="_plc.DataFormat" Label="@(_plc.Description(x => x.DataFormat))"
|
||||
Items=@(typeof(DataFormat).GetEnumList())
|
||||
MenuProps="@(props => { props.Auto = true; props.OffsetY = true; })"
|
||||
ItemText=@((u) =>u.Description)
|
||||
ItemValue=@(u =>(DataFormat)u.Value)
|
||||
HideDetails=@("auto") Height="30"
|
||||
Dense>
|
||||
</MSelect>
|
||||
|
||||
<MCheckbox Class="ma-1" Style="max-width:200px" Label=@(_plc.Description(x => x.Crc16CheckEnable)) Dense HideDetails="@("auto")" @bind-Value=@_plc.Crc16CheckEnable></MCheckbox>
|
||||
|
||||
}
|
||||
</MRow>
|
||||
</MCard>
|
||||
|
||||
|
||||
<DriverDebugUIPage @ref=_driverDebugUIPage Sections="_sections">
|
||||
|
||||
</DriverDebugUIPage>
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
#region copyright
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
#endregion
|
||||
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace ThingsGateway.Foundation.Demo;
|
||||
|
||||
|
||||
/// <inheritdoc/>
|
||||
public partial class ModbusRtuOverTcpDebugPage
|
||||
{
|
||||
/// <summary>
|
||||
/// SerialSessionPage
|
||||
/// </summary>
|
||||
private TcpClientPage _tcpClientPage;
|
||||
private DriverDebugUIPage _driverDebugUIPage;
|
||||
|
||||
private ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverTcp _plc;
|
||||
private readonly List<(string Code, string Language)> _sections = new();
|
||||
|
||||
/// <summary>
|
||||
/// <inheritdoc/>
|
||||
/// </summary>
|
||||
/// <param name="firstRender"></param>
|
||||
protected override void OnAfterRender(bool firstRender)
|
||||
{
|
||||
if (firstRender)
|
||||
{
|
||||
_sections.Add((
|
||||
"""
|
||||
private static async Task ModbusClientAsync()
|
||||
{
|
||||
//链路基础配置项
|
||||
var config = new TouchSocketConfig();
|
||||
config
|
||||
.SetRemoteIPHost(new IPHost("127.0.0.1:502"))//TCP/UDP链路才需要
|
||||
|
||||
var tcpClient1 = new TcpClient();//链路对象
|
||||
tcpClient1.Setup(config);
|
||||
|
||||
//创建协议对象,构造函数需要传入对应链路对象
|
||||
ModbusRtuOverTcp plc = new(tcpClient1)
|
||||
{
|
||||
//协议配置
|
||||
DataFormat = DataFormat.ABCD,
|
||||
FrameTime = 0,
|
||||
CacheTimeout = 1000,
|
||||
ConnectTimeOut = 3000,
|
||||
Station = 1,
|
||||
TimeOut = 3000,
|
||||
Crc16CheckEnable = true
|
||||
};
|
||||
|
||||
#region 读写测试
|
||||
var bytesResult = await plc.ReadAsync("400001", 20);
|
||||
var int32sResult = await plc.ReadInt32Async("400001", 20);
|
||||
|
||||
#endregion
|
||||
|
||||
}
|
||||
|
||||
""", "csharp"));
|
||||
|
||||
if (_tcpClientPage != null)
|
||||
_tcpClientPage.LogAction = _driverDebugUIPage.LogOut;
|
||||
_plc = new ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverTcp(_tcpClientPage.GetTcpClient());
|
||||
_driverDebugUIPage.Plc = _plc;
|
||||
//载入配置
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
base.OnAfterRender(firstRender);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
@*
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
*@
|
||||
|
||||
@page "/ModbusRtuOverUdp"
|
||||
|
||||
@using BlazorComponent;
|
||||
@using Microsoft.AspNetCore.Components.Web;
|
||||
@using Microsoft.JSInterop;
|
||||
@using Masa.Blazor;
|
||||
@namespace ThingsGateway.Foundation.Demo
|
||||
|
||||
<UdpSessionPage @ref=_udpSessionPage></UdpSessionPage>
|
||||
|
||||
<MCard Flat Class="pa-2 my-1" Style="width:100%">
|
||||
<div class="mb-4">驱动配置</div>
|
||||
<MRow Justify="JustifyTypes.Start" Align="AlignTypes.Center">
|
||||
@if (_plc != null)
|
||||
{
|
||||
|
||||
<MTextField Class="ma-1" Outlined Style="max-width:100px" Label=@(_plc.Description(x => x.FrameTime)) Dense HideDetails="@("auto")" @bind-Value=@_plc.FrameTime></MTextField>
|
||||
<MTextField Class="ma-1" Outlined Style="max-width:100px" Label=@(_plc.Description(x => x.CacheTimeout)) Dense HideDetails="@("auto")" @bind-Value=@_plc.CacheTimeout></MTextField>
|
||||
|
||||
<MTextField Class="ma-1" Outlined Style="max-width:100px" Label=@(_plc.Description(x => x.Station)) Dense HideDetails="@("auto")" @bind-Value=@_plc.Station></MTextField>
|
||||
<MTextField Class="ma-1" Outlined Style="max-width:100px" Label=@(_plc.Description(x => x.TimeOut)) Dense HideDetails="@("auto")" @bind-Value=@_plc.TimeOut></MTextField>
|
||||
|
||||
<MSelect Class="ma-1" Outlined Style="max-width:200px" @bind-Value="_plc.DataFormat" Label="@(_plc.Description(x => x.DataFormat))"
|
||||
Items=@(typeof(DataFormat).GetEnumList())
|
||||
MenuProps="@(props => { props.Auto = true; props.OffsetY = true; })"
|
||||
ItemText=@((u) =>u.Description)
|
||||
ItemValue=@(u =>(DataFormat)u.Value)
|
||||
HideDetails=@("auto") Height="30"
|
||||
Dense>
|
||||
</MSelect>
|
||||
|
||||
<MCheckbox Class="ma-1" Style="max-width:200px" Label=@(_plc.Description(x => x.Crc16CheckEnable)) Dense HideDetails="@("auto")" @bind-Value=@_plc.Crc16CheckEnable></MCheckbox>
|
||||
|
||||
}
|
||||
</MRow>
|
||||
</MCard>
|
||||
|
||||
|
||||
<DriverDebugUIPage @ref=_driverDebugUIPage Sections="_sections">
|
||||
|
||||
</DriverDebugUIPage>
|
||||
|
||||
|
||||
@code {
|
||||
private readonly List<(string Code, string Language)> _sections = new();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
#region copyright
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
#endregion
|
||||
|
||||
namespace ThingsGateway.Foundation.Demo;
|
||||
|
||||
|
||||
/// <inheritdoc/>
|
||||
public partial class ModbusRtuOverUdpDebugPage
|
||||
{
|
||||
/// <summary>
|
||||
/// SerialSessionPage
|
||||
/// </summary>
|
||||
private UdpSessionPage _udpSessionPage;
|
||||
private DriverDebugUIPage _driverDebugUIPage;
|
||||
|
||||
private ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverUdp _plc;
|
||||
|
||||
/// <summary>
|
||||
/// <inheritdoc/>
|
||||
/// </summary>
|
||||
/// <param name="firstRender"></param>
|
||||
protected override void OnAfterRender(bool firstRender)
|
||||
{
|
||||
if (firstRender)
|
||||
{
|
||||
_sections.Add((
|
||||
"""
|
||||
private static async Task ModbusClientAsync()
|
||||
{
|
||||
//链路基础配置项
|
||||
var config = new TouchSocketConfig();
|
||||
config
|
||||
.SetRemoteIPHost(new IPHost("127.0.0.1:502"))//TCP/UDP链路才需要
|
||||
|
||||
var udpSession1 = new UdpSession();//链路对象
|
||||
udpSession1.Setup(config);
|
||||
|
||||
//创建协议对象,构造函数需要传入对应链路对象
|
||||
ModbusRtuOverUdp plc = new(udpSession1)
|
||||
{
|
||||
//协议配置
|
||||
DataFormat = DataFormat.ABCD,
|
||||
FrameTime = 0,
|
||||
ConnectTimeOut = 3000,
|
||||
Station = 1,
|
||||
TimeOut = 3000,
|
||||
Crc16CheckEnable = true
|
||||
};
|
||||
|
||||
#region 读写测试
|
||||
var bytesResult = await plc.ReadAsync("400001", 20);
|
||||
var int32sResult = await plc.ReadInt32Async("400001", 20);
|
||||
|
||||
#endregion
|
||||
|
||||
}
|
||||
|
||||
""", "csharp"));
|
||||
|
||||
if (_udpSessionPage != null)
|
||||
_udpSessionPage.LogAction = _driverDebugUIPage.LogOut;
|
||||
_plc = new ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverUdp(_udpSessionPage.GetUdpSession());
|
||||
_driverDebugUIPage.Plc = _plc;
|
||||
//载入配置
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
base.OnAfterRender(firstRender);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
@*
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
*@
|
||||
|
||||
@page "/ModbusSerialServer"
|
||||
|
||||
@using BlazorComponent;
|
||||
@using Microsoft.AspNetCore.Components.Web;
|
||||
@using Microsoft.JSInterop;
|
||||
@using ThingsGateway.Foundation.Core;
|
||||
@using Masa.Blazor;
|
||||
@namespace ThingsGateway.Foundation.Demo
|
||||
|
||||
<SerialSessionPage @ref=_serialSessionPage></SerialSessionPage>
|
||||
|
||||
<MCard Flat Class="pa-2 my-1" Style="width:100%">
|
||||
<div class="mb-4">驱动配置</div>
|
||||
<MRow Justify="JustifyTypes.Start" Align="AlignTypes.Center">
|
||||
@if (_plc != null)
|
||||
{
|
||||
|
||||
<MTextField Class="ma-1" Outlined Style="max-width:100px" Label=@(_plc.Description(x => x.FrameTime)) Dense HideDetails="@("auto")" @bind-Value=@_plc.FrameTime></MTextField>
|
||||
<MTextField Class="ma-1" Outlined Style="max-width:100px" Label=@(_plc.Description(x => x.CacheTimeout)) Dense HideDetails="@("auto")" @bind-Value=@_plc.CacheTimeout></MTextField>
|
||||
|
||||
<MTextField Class="ma-1" Outlined Style="max-width:100px" Label=@(_plc.Description(x => x.Station)) Dense HideDetails="@("auto")" @bind-Value=@_plc.Station></MTextField>
|
||||
<MTextField Class="ma-1" Outlined Style="max-width:100px" Label=@(_plc.Description(x => x.TimeOut)) Dense HideDetails="@("auto")" @bind-Value=@_plc.TimeOut></MTextField>
|
||||
|
||||
<MSelect Class="ma-1" Outlined Style="max-width:200px" @bind-Value="_plc.DataFormat" Label="@(_plc.Description(x => x.DataFormat))"
|
||||
Items=@(typeof(DataFormat).GetEnumList())
|
||||
MenuProps="@(props => { props.Auto = true; props.OffsetY = true; })"
|
||||
ItemText=@((u) =>u.Description)
|
||||
ItemValue=@(u =>(DataFormat)u.Value)
|
||||
HideDetails=@("auto") Height="30"
|
||||
Dense>
|
||||
</MSelect>
|
||||
|
||||
<MCheckbox Class="ma-1" Style="max-width:200px" Label=@(_plc.Description(x => x.MulStation)) Dense HideDetails="@("auto")" @bind-Value=@_plc.MulStation></MCheckbox>
|
||||
|
||||
}
|
||||
</MRow>
|
||||
</MCard>
|
||||
|
||||
|
||||
<DriverDebugUIPage @ref=_driverDebugUIPage Sections="_sections">
|
||||
|
||||
</DriverDebugUIPage>
|
||||
|
||||
|
||||
@code {
|
||||
private readonly List<(string Code, string Language)> _sections = new();
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
#region copyright
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
#endregion
|
||||
|
||||
namespace ThingsGateway.Foundation.Demo;
|
||||
|
||||
|
||||
/// <inheritdoc/>
|
||||
public partial class ModbusSerialServerDebugPage
|
||||
{
|
||||
/// <summary>
|
||||
/// SerialSessionPage
|
||||
/// </summary>
|
||||
private SerialSessionPage _serialSessionPage;
|
||||
private DriverDebugUIPage _driverDebugUIPage;
|
||||
|
||||
private ThingsGateway.Foundation.Adapter.Modbus.ModbusSerialServer _plc;
|
||||
|
||||
/// <summary>
|
||||
/// <inheritdoc/>
|
||||
/// </summary>
|
||||
/// <param name="firstRender"></param>
|
||||
protected override void OnAfterRender(bool firstRender)
|
||||
{
|
||||
if (firstRender)
|
||||
{
|
||||
_sections.Add((
|
||||
"""
|
||||
private static async Task ModbusClientAsync()
|
||||
{
|
||||
//链路基础配置项
|
||||
var config = new TouchSocketConfig();
|
||||
config
|
||||
.SetSerialProperty(new SerialProperty() //串口链路才需要
|
||||
{
|
||||
PortName = "COM1"
|
||||
});
|
||||
var serialSession = new SerialSession();//链路对象
|
||||
serialSession.Setup(config);
|
||||
|
||||
//创建协议对象,构造函数需要传入对应链路对象
|
||||
ModbusSerialServer plc = new(serialSession)//传入链路
|
||||
{
|
||||
//协议配置
|
||||
DataFormat = DataFormat.ABCD,
|
||||
FrameTime = 0,
|
||||
CacheTimeout = 1000,
|
||||
ConnectTimeOut = 3000,
|
||||
Station = 1,
|
||||
TimeOut = 3000,
|
||||
Crc16CheckEnable = true
|
||||
};
|
||||
|
||||
#region 读写测试
|
||||
var bytesResult = await plc.ReadAsync("400001", 20);
|
||||
var int32sResult = await plc.ReadInt32Async("400001", 20);
|
||||
|
||||
#endregion
|
||||
|
||||
}
|
||||
|
||||
""", "csharp"));
|
||||
|
||||
|
||||
if (_serialSessionPage != null)
|
||||
_serialSessionPage.LogAction = _driverDebugUIPage.LogOut;
|
||||
_plc = new ThingsGateway.Foundation.Adapter.Modbus.ModbusSerialServer(_serialSessionPage.GetSerialSession());
|
||||
_driverDebugUIPage.Plc = _plc;
|
||||
//载入配置
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
base.OnAfterRender(firstRender);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
@*
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
*@
|
||||
|
||||
@page "/ModbusTcp"
|
||||
|
||||
@using BlazorComponent;
|
||||
@using Microsoft.AspNetCore.Components.Web;
|
||||
@using Microsoft.JSInterop;
|
||||
@using Masa.Blazor;
|
||||
@namespace ThingsGateway.Foundation.Demo
|
||||
|
||||
<TcpClientPage @ref=_tcpClientPage></TcpClientPage>
|
||||
|
||||
<MCard Flat Class="pa-2 my-1" Style="width:100%">
|
||||
<div class="mb-4">驱动配置</div>
|
||||
<MRow Justify="JustifyTypes.Start" Align="AlignTypes.Center">
|
||||
@if (_plc != null)
|
||||
{
|
||||
|
||||
<MTextField Class="ma-1" Outlined Style="max-width:100px" Label=@(_plc.Description(x => x.FrameTime)) Dense HideDetails="@("auto")" @bind-Value=@_plc.FrameTime></MTextField>
|
||||
<MTextField Class="ma-1" Outlined Style="max-width:100px" Label=@(_plc.Description(x => x.CacheTimeout)) Dense HideDetails="@("auto")" @bind-Value=@_plc.CacheTimeout></MTextField>
|
||||
|
||||
<MTextField Class="ma-1" Outlined Style="max-width:100px" Label=@(_plc.Description(x => x.Station)) Dense HideDetails="@("auto")" @bind-Value=@_plc.Station></MTextField>
|
||||
<MTextField Class="ma-1" Outlined Style="max-width:100px" Label=@(_plc.Description(x => x.TimeOut)) Dense HideDetails="@("auto")" @bind-Value=@_plc.TimeOut></MTextField>
|
||||
|
||||
<MSelect Class="ma-1" Outlined Style="max-width:200px" @bind-Value="_plc.DataFormat" Label="@(_plc.Description(x => x.DataFormat))"
|
||||
Items=@(typeof(DataFormat).GetEnumList())
|
||||
MenuProps="@(props => { props.Auto = true; props.OffsetY = true; })"
|
||||
ItemText=@((u) =>u.Description)
|
||||
ItemValue=@(u =>(DataFormat)u.Value)
|
||||
HideDetails=@("auto") Height="30"
|
||||
Dense>
|
||||
</MSelect>
|
||||
|
||||
<MCheckbox Class="ma-1" Style="max-width:200px" Label=@(_plc.Description(x => x.IsCheckMessageId)) Dense HideDetails="@("auto")" @bind-Value=@_plc.IsCheckMessageId></MCheckbox>
|
||||
|
||||
}
|
||||
</MRow>
|
||||
</MCard>
|
||||
|
||||
|
||||
<DriverDebugUIPage @ref=_driverDebugUIPage Sections="_sections">
|
||||
|
||||
</DriverDebugUIPage>
|
||||
|
||||
|
||||
@code {
|
||||
private readonly List<(string Code, string Language)> _sections = new();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
#region copyright
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
#endregion
|
||||
|
||||
namespace ThingsGateway.Foundation.Demo;
|
||||
|
||||
|
||||
/// <inheritdoc/>
|
||||
public partial class ModbusTcpDebugPage
|
||||
{
|
||||
private DriverDebugUIPage _driverDebugUIPage;
|
||||
|
||||
private ThingsGateway.Foundation.Adapter.Modbus.ModbusTcp _plc;
|
||||
|
||||
/// <summary>
|
||||
/// SerialSessionPage
|
||||
/// </summary>
|
||||
private TcpClientPage _tcpClientPage;
|
||||
/// <summary>
|
||||
/// <inheritdoc/>
|
||||
/// </summary>
|
||||
/// <param name="firstRender"></param>
|
||||
protected override void OnAfterRender(bool firstRender)
|
||||
{
|
||||
if (firstRender)
|
||||
{
|
||||
_sections.Add((
|
||||
"""
|
||||
private static async Task ModbusClientAsync()
|
||||
{
|
||||
//链路基础配置项
|
||||
var config = new TouchSocketConfig();
|
||||
config
|
||||
.SetRemoteIPHost(new IPHost("127.0.0.1:502"))//TCP/UDP链路才需要
|
||||
|
||||
var tcpClient1 = new TcpClient();//链路对象
|
||||
tcpClient1.Setup(config);
|
||||
|
||||
//创建协议对象,构造函数需要传入对应链路对象
|
||||
ModbusTcp plc = new(tcpClient1)
|
||||
{
|
||||
//协议配置
|
||||
DataFormat = DataFormat.ABCD,
|
||||
FrameTime = 0,
|
||||
CacheTimeout = 1000,
|
||||
ConnectTimeOut = 3000,
|
||||
Station = 1,
|
||||
TimeOut = 3000,
|
||||
IsCheckMessageId = true
|
||||
};
|
||||
|
||||
#region 读写测试
|
||||
var bytesResult = await plc.ReadAsync("400001", 20);
|
||||
var int32sResult = await plc.ReadInt32Async("400001", 20);
|
||||
|
||||
#endregion
|
||||
|
||||
}
|
||||
|
||||
""", "csharp"));
|
||||
|
||||
if (_tcpClientPage != null)
|
||||
_tcpClientPage.LogAction = _driverDebugUIPage.LogOut;
|
||||
_plc = new ThingsGateway.Foundation.Adapter.Modbus.ModbusTcp(_tcpClientPage.GetTcpClient());
|
||||
_driverDebugUIPage.Plc = _plc;
|
||||
//载入配置
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
base.OnAfterRender(firstRender);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
@*
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
*@
|
||||
|
||||
@page "/ModbusTcpDtu"
|
||||
|
||||
@using BlazorComponent;
|
||||
@using Microsoft.AspNetCore.Components.Web;
|
||||
@using Microsoft.JSInterop;
|
||||
@using Masa.Blazor;
|
||||
@namespace ThingsGateway.Foundation.Demo
|
||||
|
||||
<TcpServerPage @ref=_tcpServerPage></TcpServerPage>
|
||||
|
||||
<MCard Flat Class="pa-2 my-1" Style="width:100%">
|
||||
<div class="mb-4">驱动配置</div>
|
||||
<MRow Justify="JustifyTypes.Start" Align="AlignTypes.Center">
|
||||
@if (_plc != null)
|
||||
{
|
||||
<MTextField Class="ma-1" Outlined Style="max-width:100px" Label=@(_plc.Description(x => x.FrameTime)) Dense HideDetails="@("auto")" @bind-Value=@_plc.FrameTime></MTextField>
|
||||
<MTextField Class="ma-1" Outlined Style="max-width:100px" Label=@(_plc.Description(x => x.CacheTimeout)) Dense HideDetails="@("auto")" @bind-Value=@_plc.CacheTimeout></MTextField>
|
||||
|
||||
<MTextField Class="ma-1" Outlined Style="max-width:100px" Label=@(_plc.Description(x => x.Station)) Dense HideDetails="@("auto")" @bind-Value=@_plc.Station></MTextField>
|
||||
<MTextField Class="ma-1" Outlined Style="max-width:100px" Label=@(_plc.Description(x => x.TimeOut)) Dense HideDetails="@("auto")" @bind-Value=@_plc.TimeOut></MTextField>
|
||||
|
||||
<MSelect Class="ma-1" Outlined Style="max-width:200px" @bind-Value="_plc.DataFormat" Label="@(_plc.Description(x => x.DataFormat))"
|
||||
Items=@(typeof(DataFormat).GetEnumList())
|
||||
MenuProps="@(props => { props.Auto = true; props.OffsetY = true; })"
|
||||
ItemText=@((u) =>u.Description)
|
||||
ItemValue=@(u =>(DataFormat)u.Value)
|
||||
HideDetails=@("auto") Height="30"
|
||||
Dense>
|
||||
</MSelect>
|
||||
|
||||
<MCheckbox Class="ma-1" Style="max-width:200px" Label=@(_plc.Description(x => x.IsCheckMessageId)) Dense HideDetails="@("auto")" @bind-Value=@_plc.IsCheckMessageId></MCheckbox>
|
||||
|
||||
}
|
||||
</MRow>
|
||||
</MCard>
|
||||
|
||||
|
||||
<DriverDebugUIPage @ref=_driverDebugUIPage Sections="_sections">
|
||||
|
||||
</DriverDebugUIPage>
|
||||
|
||||
|
||||
@code {
|
||||
private readonly List<(string Code, string Language)> _sections = new();
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
#region copyright
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
#endregion
|
||||
|
||||
namespace ThingsGateway.Foundation.Demo;
|
||||
|
||||
/// <summary>
|
||||
/// ModbusTcpDtuDebugPage
|
||||
/// </summary>
|
||||
public partial class ModbusTcpDtuDebugPage
|
||||
{
|
||||
/// <summary>
|
||||
/// TcpServerPage
|
||||
/// </summary>
|
||||
private TcpServerPage _tcpServerPage;
|
||||
private DriverDebugUIPage _driverDebugUIPage;
|
||||
|
||||
private ThingsGateway.Foundation.Adapter.Modbus.ModbusTcpDtu _plc;
|
||||
|
||||
/// <summary>
|
||||
/// <inheritdoc/>
|
||||
/// </summary>
|
||||
/// <param name="firstRender"></param>
|
||||
protected override void OnAfterRender(bool firstRender)
|
||||
{
|
||||
if (firstRender)
|
||||
{
|
||||
_sections.Add((
|
||||
"""
|
||||
private static async Task ModbusClientAsync()
|
||||
{
|
||||
//链路基础配置项
|
||||
var config = new TouchSocketConfig();
|
||||
config
|
||||
.SetRemoteIPHost(new IPHost("127.0.0.1:502"))//TCP/UDP链路才需要
|
||||
|
||||
var tcpClient1 = new TcpClient();//链路对象
|
||||
tcpClient1.Setup(config);
|
||||
|
||||
//创建协议对象,构造函数需要传入对应链路对象
|
||||
ModbusTcpDtu plc = new(tcpClient1)
|
||||
{
|
||||
//协议配置
|
||||
DataFormat = DataFormat.ABCD,
|
||||
FrameTime = 0,
|
||||
CacheTimeout = 1000,
|
||||
ConnectTimeOut = 3000,
|
||||
Station = 1,
|
||||
TimeOut = 3000,
|
||||
IsCheckMessageId = true
|
||||
};
|
||||
|
||||
#region 读写测试
|
||||
var bytesResult = await plc.ReadAsync("400001", 20);
|
||||
var int32sResult = await plc.ReadInt32Async("400001", 20);
|
||||
|
||||
#endregion
|
||||
|
||||
}
|
||||
|
||||
""", "csharp"));
|
||||
|
||||
if (_tcpServerPage != null)
|
||||
_tcpServerPage.LogAction = _driverDebugUIPage.LogOut;
|
||||
_plc = new ThingsGateway.Foundation.Adapter.Modbus.ModbusTcpDtu(_tcpServerPage.GetTcpServer());
|
||||
_driverDebugUIPage.Plc = _plc;
|
||||
//载入配置
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
base.OnAfterRender(firstRender);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
@*
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
*@
|
||||
|
||||
@page "/ModbusTcpServer"
|
||||
|
||||
@using BlazorComponent;
|
||||
@using Microsoft.AspNetCore.Components.Web;
|
||||
@using Microsoft.JSInterop;
|
||||
@using Masa.Blazor;
|
||||
@namespace ThingsGateway.Foundation.Demo
|
||||
|
||||
<TcpServerPage @ref=_tcpServerPage></TcpServerPage>
|
||||
|
||||
<MCard Flat Class="pa-2 my-1" Style="width:100%">
|
||||
<div class="mb-4">驱动配置</div>
|
||||
<MRow Justify="JustifyTypes.Start" Align="AlignTypes.Center">
|
||||
@if (_plc != null)
|
||||
{
|
||||
<MTextField Class="ma-1" Outlined Style="max-width:100px" Label=@(_plc.Description(x => x.FrameTime)) Dense HideDetails="@("auto")" @bind-Value=@_plc.FrameTime></MTextField>
|
||||
<MTextField Class="ma-1" Outlined Style="max-width:100px" Label=@(_plc.Description(x => x.CacheTimeout)) Dense HideDetails="@("auto")" @bind-Value=@_plc.CacheTimeout></MTextField>
|
||||
|
||||
<MTextField Class="ma-1" Outlined Style="max-width:100px" Label=@(_plc.Description(x => x.Station)) Dense HideDetails="@("auto")" @bind-Value=@_plc.Station></MTextField>
|
||||
<MTextField Class="ma-1" Outlined Style="max-width:100px" Label=@(_plc.Description(x => x.TimeOut)) Dense HideDetails="@("auto")" @bind-Value=@_plc.TimeOut></MTextField>
|
||||
|
||||
<MSelect Class="ma-1" Outlined Style="max-width:200px" @bind-Value="_plc.DataFormat" Label="@(_plc.Description(x => x.DataFormat))"
|
||||
Items=@(typeof(DataFormat).GetEnumList())
|
||||
MenuProps="@(props => { props.Auto = true; props.OffsetY = true; })"
|
||||
ItemText=@((u) =>u.Description)
|
||||
ItemValue=@(u =>(DataFormat)u.Value)
|
||||
HideDetails=@("auto") Height="30"
|
||||
Dense>
|
||||
</MSelect>
|
||||
|
||||
<MCheckbox Class="ma-1" Style="max-width:200px" Label=@(_plc.Description(x => x.MulStation)) Dense HideDetails="@("auto")" @bind-Value=@_plc.MulStation></MCheckbox>
|
||||
|
||||
}
|
||||
</MRow>
|
||||
</MCard>
|
||||
|
||||
|
||||
<DriverDebugUIPage @ref=_driverDebugUIPage Sections="_sections">
|
||||
|
||||
</DriverDebugUIPage>
|
||||
|
||||
|
||||
@code {
|
||||
private readonly List<(string Code, string Language)> _sections = new();
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
#region copyright
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
#endregion
|
||||
|
||||
namespace ThingsGateway.Foundation.Demo;
|
||||
|
||||
/// <summary>
|
||||
/// ModbusTcpServerDebugPage
|
||||
/// </summary>
|
||||
public partial class ModbusTcpServerDebugPage
|
||||
{
|
||||
/// <summary>
|
||||
/// TcpServerPage
|
||||
/// </summary>
|
||||
private TcpServerPage _tcpServerPage;
|
||||
private DriverDebugUIPage _driverDebugUIPage;
|
||||
|
||||
private ThingsGateway.Foundation.Adapter.Modbus.ModbusTcpServer _plc;
|
||||
|
||||
/// <summary>
|
||||
/// <inheritdoc/>
|
||||
/// </summary>
|
||||
/// <param name="firstRender"></param>
|
||||
protected override void OnAfterRender(bool firstRender)
|
||||
{
|
||||
if (firstRender)
|
||||
{
|
||||
_sections.Add((
|
||||
"""
|
||||
private static async Task ModbusClientAsync()
|
||||
{
|
||||
//链路基础配置项
|
||||
var config = new TouchSocketConfig();
|
||||
config
|
||||
.SetRemoteIPHost(new IPHost("127.0.0.1:502"))//TCP/UDP链路才需要
|
||||
|
||||
var tcpClient1 = new TcpClient();//链路对象
|
||||
tcpClient1.Setup(config);
|
||||
|
||||
//创建协议对象,构造函数需要传入对应链路对象
|
||||
ModbusTcpServer plc = new(tcpClient1)
|
||||
{
|
||||
//协议配置
|
||||
DataFormat = DataFormat.ABCD,
|
||||
FrameTime = 0,
|
||||
CacheTimeout = 1000,
|
||||
ConnectTimeOut = 3000,
|
||||
Station = 1,
|
||||
TimeOut = 3000,
|
||||
IsCheckMessageId = true
|
||||
};
|
||||
|
||||
#region 读写测试
|
||||
var bytesResult = await plc.ReadAsync("400001", 20);
|
||||
var int32sResult = await plc.ReadInt32Async("400001", 20);
|
||||
|
||||
#endregion
|
||||
|
||||
}
|
||||
|
||||
""", "csharp"));
|
||||
|
||||
if (_tcpServerPage != null)
|
||||
_tcpServerPage.LogAction = _driverDebugUIPage.LogOut;
|
||||
_plc = new ThingsGateway.Foundation.Adapter.Modbus.ModbusTcpServer(_tcpServerPage.GetTcpServer());
|
||||
_driverDebugUIPage.Plc = _plc;
|
||||
//载入配置
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
base.OnAfterRender(firstRender);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
@*
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
*@
|
||||
|
||||
@page "/ModbusUdp"
|
||||
|
||||
@using BlazorComponent;
|
||||
@using Microsoft.AspNetCore.Components.Web;
|
||||
@using Microsoft.JSInterop;
|
||||
@using Masa.Blazor;
|
||||
@namespace ThingsGateway.Foundation.Demo
|
||||
|
||||
<UdpSessionPage @ref=_udpSessionPage></UdpSessionPage>
|
||||
|
||||
<MCard Flat Class="pa-2 my-1" Style="width:100%">
|
||||
<div class="mb-4">驱动配置</div>
|
||||
<MRow Justify="JustifyTypes.Start" Align="AlignTypes.Center">
|
||||
@if (_plc != null)
|
||||
{
|
||||
|
||||
<MTextField Class="ma-1" Outlined Style="max-width:100px" Label=@(_plc.Description(x => x.FrameTime)) Dense HideDetails="@("auto")" @bind-Value=@_plc.FrameTime></MTextField>
|
||||
<MTextField Class="ma-1" Outlined Style="max-width:100px" Label=@(_plc.Description(x => x.CacheTimeout)) Dense HideDetails="@("auto")" @bind-Value=@_plc.CacheTimeout></MTextField>
|
||||
|
||||
<MTextField Class="ma-1" Outlined Style="max-width:100px" Label=@(_plc.Description(x => x.Station)) Dense HideDetails="@("auto")" @bind-Value=@_plc.Station></MTextField>
|
||||
<MTextField Class="ma-1" Outlined Style="max-width:100px" Label=@(_plc.Description(x => x.TimeOut)) Dense HideDetails="@("auto")" @bind-Value=@_plc.TimeOut></MTextField>
|
||||
|
||||
<MSelect Class="ma-1" Outlined Style="max-width:200px" @bind-Value="_plc.DataFormat" Label="@(_plc.Description(x => x.DataFormat))"
|
||||
Items=@(typeof(DataFormat).GetEnumList())
|
||||
MenuProps="@(props => { props.Auto = true; props.OffsetY = true; })"
|
||||
ItemText=@((u) =>u.Description)
|
||||
ItemValue=@(u =>(DataFormat)u.Value)
|
||||
HideDetails=@("auto") Height="30"
|
||||
Dense>
|
||||
</MSelect>
|
||||
|
||||
<MCheckbox Class="ma-1" Style="max-width:200px" Label=@(_plc.Description(x => x.IsCheckMessageId)) Dense HideDetails="@("auto")" @bind-Value=@_plc.IsCheckMessageId></MCheckbox>
|
||||
|
||||
}
|
||||
</MRow>
|
||||
</MCard>
|
||||
|
||||
|
||||
<DriverDebugUIPage @ref=_driverDebugUIPage Sections="_sections">
|
||||
|
||||
</DriverDebugUIPage>
|
||||
|
||||
|
||||
@code {
|
||||
private readonly List<(string Code, string Language)> _sections = new();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
#region copyright
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
#endregion
|
||||
|
||||
namespace ThingsGateway.Foundation.Demo;
|
||||
|
||||
|
||||
/// <inheritdoc/>
|
||||
public partial class ModbusUdpDebugPage
|
||||
{
|
||||
/// <summary>
|
||||
/// SerialSessionPage
|
||||
/// </summary>
|
||||
private UdpSessionPage _udpSessionPage;
|
||||
private DriverDebugUIPage _driverDebugUIPage;
|
||||
|
||||
private ThingsGateway.Foundation.Adapter.Modbus.ModbusUdp _plc;
|
||||
|
||||
/// <summary>
|
||||
/// <inheritdoc/>
|
||||
/// </summary>
|
||||
/// <param name="firstRender"></param>
|
||||
protected override void OnAfterRender(bool firstRender)
|
||||
{
|
||||
if (firstRender)
|
||||
{
|
||||
_sections.Add((
|
||||
"""
|
||||
private static async Task ModbusClientAsync()
|
||||
{
|
||||
//链路基础配置项
|
||||
var config = new TouchSocketConfig();
|
||||
config
|
||||
.SetRemoteIPHost(new IPHost("127.0.0.1:502"))//TCP/UDP链路才需要
|
||||
|
||||
var udpSession1 = new UdpSession();//链路对象
|
||||
udpSession1.Setup(config);
|
||||
|
||||
//创建协议对象,构造函数需要传入对应链路对象
|
||||
ModbusUdp plc = new(udpSession1)
|
||||
{
|
||||
//协议配置
|
||||
DataFormat = DataFormat.ABCD,
|
||||
FrameTime = 0,
|
||||
ConnectTimeOut = 3000,
|
||||
Station = 1,
|
||||
TimeOut = 3000,
|
||||
Crc16CheckEnable = true
|
||||
};
|
||||
|
||||
#region 读写测试
|
||||
var bytesResult = await plc.ReadAsync("400001", 20);
|
||||
var int32sResult = await plc.ReadInt32Async("400001", 20);
|
||||
|
||||
#endregion
|
||||
|
||||
}
|
||||
|
||||
""", "csharp"));
|
||||
|
||||
if (_udpSessionPage != null)
|
||||
_udpSessionPage.LogAction = _driverDebugUIPage.LogOut;
|
||||
_plc = new ThingsGateway.Foundation.Adapter.Modbus.ModbusUdp(_udpSessionPage.GetUdpSession());
|
||||
_driverDebugUIPage.Plc = _plc;
|
||||
//载入配置
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
base.OnAfterRender(firstRender);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
@*
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
*@
|
||||
|
||||
@page "/OPCDAClient"
|
||||
|
||||
@namespace ThingsGateway.Foundation.Demo
|
||||
@using BlazorComponent;
|
||||
@using Microsoft.AspNetCore.Components.Web;
|
||||
@using Masa.Blazor.Presets;
|
||||
@using Microsoft.JSInterop;
|
||||
@inherits BaseComponentBase
|
||||
|
||||
|
||||
@using ThingsGateway.Foundation.Core;
|
||||
@using ThingsGateway.Foundation.Adapter.OPCDA.Da;
|
||||
@using ThingsGateway.Foundation.Extension;
|
||||
@using ThingsGateway.Foundation.Serial;
|
||||
@using Masa.Blazor
|
||||
|
||||
|
||||
|
||||
<OPCDAClientPage @ref=opcDAClientPage></OPCDAClientPage>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<DriverDebugUIPage @ref=_driverDebugUIPage Sections="_sections" ShowDefaultOtherContent=false>
|
||||
<ReadWriteContent>
|
||||
<div class="my-1 py-1">
|
||||
|
||||
<MTextarea Class="mx-1 my-1" Label="变量地址" Dense Outlined HideDetails="@("auto")" @bind-Value=@_driverDebugUIPage.Address />
|
||||
|
||||
<MButton Class="mx-1 my-1" Color="primary" OnClick="Add">
|
||||
添加
|
||||
</MButton>
|
||||
<MButton Class="mx-1 my-1" Color="primary" OnClick="Remove">
|
||||
移除
|
||||
</MButton>
|
||||
<MButton Class="mx-1 my-1" Color="primary" OnClick="ReadAsync">
|
||||
组读取
|
||||
</MButton>
|
||||
|
||||
<MTextarea Class="mx-1 mt-3 my-1" Label="值" Dense Outlined HideDetails="@("auto")" @bind-Value=@_driverDebugUIPage.WriteValue />
|
||||
<MButton Class="mx-1 my-1" Color="primary" OnClick="WriteAsync">
|
||||
写入
|
||||
</MButton>
|
||||
|
||||
<MButton Class="mx-1 my-3" Color="accent" OnClick="()=>IsShowImportVariableList=!IsShowImportVariableList">
|
||||
<span>查看OPC节点空间</span>
|
||||
</MButton>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<MCol Class="my-1 py-1">
|
||||
<MRow NoGutters>
|
||||
|
||||
|
||||
|
||||
</MRow>
|
||||
|
||||
</MCol>
|
||||
|
||||
</ReadWriteContent>
|
||||
</DriverDebugUIPage>
|
||||
|
||||
|
||||
@{
|
||||
#if Plugin
|
||||
<PModal @bind-Value="IsShowImportVariableList" OnCancel="() => IsShowImportVariableList = false"
|
||||
Title=导入OPC变量 Height=@("700") Persistent
|
||||
MaxWidth="1500"
|
||||
OnSave="DownDeviceExport">
|
||||
|
||||
<SaveContent Context="save">
|
||||
|
||||
<MMenu OffsetY Context="menu">
|
||||
<ActivatorContent>
|
||||
<MButton @attributes="@menu.Attrs" Color="primary" Class="my-1 mx-2 " Disabled="isDownLoading" Loading="isDownLoading">
|
||||
导出
|
||||
<AppChevronDown></AppChevronDown>
|
||||
</MButton>
|
||||
</ActivatorContent>
|
||||
<ChildContent>
|
||||
<MList>
|
||||
<MListItem Color="primary" OnClick="DownDeviceExport" Disabled="isDownLoading" Loading="isDownLoading"> 导出到excel </MListItem>
|
||||
<MListItem Color="primary" OnClick="DeviceImport" Disabled="isDownLoading" Loading="isDownLoading">导入到系统</MListItem>
|
||||
</MList>
|
||||
|
||||
</ChildContent>
|
||||
</MMenu>
|
||||
|
||||
</SaveContent>
|
||||
|
||||
<ChildContent>
|
||||
@if (IsShowImportVariableList)
|
||||
{
|
||||
<OPCDAImportVariable @ref=ImportVariable PLC="_plc"></OPCDAImportVariable>
|
||||
}
|
||||
</ChildContent>
|
||||
|
||||
</PModal>
|
||||
|
||||
#else
|
||||
<PModal @bind-Value="IsShowImportVariableList" OnCancel="() => IsShowImportVariableList = false"
|
||||
Title=浏览OPC变量 Height=@("700") Persistent
|
||||
MaxWidth="1500">
|
||||
|
||||
<ChildContent>
|
||||
@if (IsShowImportVariableList)
|
||||
{
|
||||
<OPCDAImportVariable @ref=ImportVariable PLC="_plc"></OPCDAImportVariable>
|
||||
}
|
||||
</ChildContent>
|
||||
|
||||
</PModal>
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@code {
|
||||
private readonly List<(string Code, string Language)> _sections = new();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,234 @@
|
||||
#region copyright
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
#endregion
|
||||
|
||||
using BlazorComponent;
|
||||
|
||||
using Masa.Blazor;
|
||||
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.JSInterop;
|
||||
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
using System.Collections.Generic;
|
||||
|
||||
using ThingsGateway.Foundation.Adapter.OPCDA;
|
||||
using ThingsGateway.Foundation.Adapter.OPCDA.Da;
|
||||
|
||||
namespace ThingsGateway.Foundation.Demo;
|
||||
|
||||
/// <summary>
|
||||
/// OPCDA调试页面
|
||||
/// </summary>
|
||||
public partial class OPCDAClientDebugPage : IDisposable
|
||||
{
|
||||
private ThingsGateway.Foundation.Adapter.OPCDA.OPCDAClient _plc;
|
||||
private DriverDebugUIPage _driverDebugUIPage;
|
||||
bool IsShowImportVariableList;
|
||||
private OPCDAClientPage opcDAClientPage;
|
||||
private OPCDAImportVariable ImportVariable { get; set; }
|
||||
[Inject]
|
||||
private InitTimezone InitTimezone { get; set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void Dispose()
|
||||
{
|
||||
_plc.SafeDispose();
|
||||
opcDAClientPage.SafeDispose();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string ToString()
|
||||
{
|
||||
return nameof(OPCDAClient);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void OnAfterRender(bool firstRender)
|
||||
{
|
||||
if (firstRender)
|
||||
{
|
||||
if (opcDAClientPage != null)
|
||||
{
|
||||
opcDAClientPage.LogAction = _driverDebugUIPage.LogOut;
|
||||
opcDAClientPage.ValueAction = ValueOut;
|
||||
|
||||
}
|
||||
_plc = opcDAClientPage.OPC;
|
||||
//载入配置
|
||||
StateHasChanged();
|
||||
_driverDebugUIPage.Sections.Clear();
|
||||
}
|
||||
|
||||
base.OnAfterRender(firstRender);
|
||||
}
|
||||
|
||||
private void Add()
|
||||
{
|
||||
var tags = new Dictionary<string, List<OpcItem>>();
|
||||
var tag = new OpcItem(_driverDebugUIPage.Address);
|
||||
tags.Add(Guid.NewGuid().ToString(), new List<OpcItem>() { tag });
|
||||
try
|
||||
{
|
||||
_plc.AddItems(tags);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_driverDebugUIPage.Messages.Add((Microsoft.Extensions.Logging.LogLevel.Warning, $"{DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(InitTimezone.TimezoneOffset)} - {ex}"));
|
||||
}
|
||||
}
|
||||
|
||||
#if Plugin
|
||||
private bool isDownLoading;
|
||||
private async Task DeviceImport()
|
||||
{
|
||||
isDownLoading = true;
|
||||
await InvokeAsync(StateHasChanged);
|
||||
try
|
||||
{
|
||||
|
||||
var data = ImportVariable?.GetImportVariableList();
|
||||
if (data != null)
|
||||
{
|
||||
if (data?.Item2?.Count == 0)
|
||||
{
|
||||
await PopupService.EnqueueSnackbarAsync("无可用变量", AlertTypes.Warning);
|
||||
return;
|
||||
}
|
||||
await _serviceScope.ServiceProvider.GetService<CollectDeviceService>().AddAsync(data?.Item1);
|
||||
await _serviceScope.ServiceProvider.GetService<VariableService>().AddBatchAsync(data?.Item2);
|
||||
await PopupService.EnqueueSnackbarAsync("成功", AlertTypes.Success);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
isDownLoading = false;
|
||||
}
|
||||
}
|
||||
private IJSObjectReference JSObjectReference;
|
||||
|
||||
/// <inheritdoc/>
|
||||
[Inject]
|
||||
protected IJSRuntime JSRuntime { get; set; }
|
||||
private async Task DownDeviceExport()
|
||||
{
|
||||
isDownLoading = true;
|
||||
await InvokeAsync(StateHasChanged);
|
||||
try
|
||||
{
|
||||
var data = ImportVariable?.GetImportVariableList();
|
||||
if (data != null)
|
||||
{
|
||||
if (data?.Item2?.Count == 0)
|
||||
{
|
||||
await PopupService.EnqueueSnackbarAsync("无可用变量", AlertTypes.Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
await DownDeviceExportAsync(data?.Item1);
|
||||
await DownDeviceVariableExportAsync(data?.Item2, data?.Item1.Name);
|
||||
await PopupService.EnqueueSnackbarAsync("成功", AlertTypes.Success);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
isDownLoading = false;
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// 导出到excel
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public async Task DownDeviceExportAsync(CollectDevice data)
|
||||
{
|
||||
using var memoryStream = await _serviceScope.ServiceProvider.GetService<CollectDeviceService>().ExportFileAsync(new List<CollectDevice>() { data });
|
||||
using var streamRef = new DotNetStreamReference(stream: memoryStream);
|
||||
JSObjectReference ??= await JSRuntime.LoadModuleAsync("js/downloadFileFromStream");
|
||||
await JSObjectReference.InvokeVoidAsync("downloadFileFromStream", $"设备导出{DateTimeExtensions.CurrentDateTime.ToFileDateTimeFormat()}.xlsx", streamRef);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 导出到excel
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public async Task DownDeviceVariableExportAsync(List<DeviceVariable> data, string devName)
|
||||
{
|
||||
using var memoryStream = await _serviceScope.ServiceProvider.GetService<VariableService>().ExportFileAsync(data, devName);
|
||||
using var streamRef = new DotNetStreamReference(stream: memoryStream);
|
||||
JSObjectReference ??= await JSRuntime.LoadModuleAsync("js/downloadFileFromStream");
|
||||
await JSObjectReference.InvokeVoidAsync("downloadFileFromStream", $"变量导出{DateTimeExtensions.CurrentDateTime.ToFileDateTimeFormat()}.xlsx", streamRef);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
private async Task ReadAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
_plc.ReadItemsWithGroup();
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_driverDebugUIPage.Messages.Add((Microsoft.Extensions.Logging.LogLevel.Warning, $"{DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(InitTimezone.TimezoneOffset)} - {ex}"));
|
||||
}
|
||||
|
||||
await Task.CompletedTask;
|
||||
}
|
||||
private void Remove()
|
||||
{
|
||||
_plc.RemoveItems(new List<string>() { _driverDebugUIPage.Address });
|
||||
}
|
||||
|
||||
private void ValueOut(List<ItemReadResult> values)
|
||||
{
|
||||
_driverDebugUIPage.Messages.Add((Microsoft.Extensions.Logging.LogLevel.Debug, DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(InitTimezone.TimezoneOffset) + " - " + values.ToJsonString()));
|
||||
if (_driverDebugUIPage.Messages.Count > 2500)
|
||||
{
|
||||
_driverDebugUIPage.Messages.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
private async Task WriteAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
JToken tagValue = _driverDebugUIPage.WriteValue.GetJTokenFromObj();
|
||||
var obj = tagValue.GetObjFromJToken();
|
||||
|
||||
var data = _plc.WriteItem(
|
||||
new()
|
||||
{
|
||||
{_driverDebugUIPage.Address, obj}
|
||||
}
|
||||
);
|
||||
if (data.Count > 0)
|
||||
{
|
||||
foreach (var item in data)
|
||||
{
|
||||
_driverDebugUIPage.Messages.Add((Microsoft.Extensions.Logging.LogLevel.Warning, DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(InitTimezone.TimezoneOffset) + " - " + item.ToJsonString()));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_driverDebugUIPage.Messages.Add((Microsoft.Extensions.Logging.LogLevel.Information, DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(InitTimezone.TimezoneOffset) + " - 写入成功"));
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_driverDebugUIPage.Messages.Add((Microsoft.Extensions.Logging.LogLevel.Error, $"{DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(InitTimezone.TimezoneOffset)} - 写入失败:{ex}"));
|
||||
}
|
||||
|
||||
await Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
@*
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
*@
|
||||
|
||||
@namespace ThingsGateway.Foundation.Demo
|
||||
@using BlazorComponent;
|
||||
@using Microsoft.AspNetCore.Components.Web;
|
||||
@using System.IO.Ports;
|
||||
@using System.Collections.Concurrent;
|
||||
@using ThingsGateway.Foundation.Core;
|
||||
@using ThingsGateway.Foundation.Adapter.OPCDA;
|
||||
@using Masa.Blazor
|
||||
@implements IDisposable
|
||||
<MCard Flat Class="pa-2 my-1" Style="width:100%">
|
||||
<div class="mb-4">设备配置</div>
|
||||
<MRow Justify="JustifyTypes.Start" Align="AlignTypes.Center">
|
||||
<MTextField Class="ma-1" Label=@node.Description(a=>a.GroupSize) Dense Outlined HideDetails="@("auto")" @bind-Value=@node.GroupSize />
|
||||
<MTextField Class="ma-1" Label=@node.Description(a=>a.UpdateRate) Dense Outlined HideDetails="@("auto")" @bind-Value=@node.UpdateRate />
|
||||
|
||||
<MTextField Class="ma-1" Label=@node.Description(a=>a.DeadBand) Dense Outlined HideDetails="@("auto")" @bind-Value=@node.DeadBand />
|
||||
<MTextField Class="ma-1" Label=@node.Description(a=>a.CheckRate) Dense Outlined HideDetails="@("auto")" @bind-Value=@node.CheckRate />
|
||||
<MTextField Class="ma-1" Label=@node.Description(a=>a.OPCIP) Dense Outlined HideDetails="@("auto")" @bind-Value=@node.OPCIP />
|
||||
<MTextField Class="ma-1" Label=@node.Description(a=>a.OPCName) Dense Outlined HideDetails="@("auto")" @bind-Value=@node.OPCName />
|
||||
|
||||
<MCheckbox Class="ma-1" Label=@node.Description(a=>a.ActiveSubscribe) Dense Outlined HideDetails="@("auto")" @bind-Value=@node.ActiveSubscribe />
|
||||
|
||||
<MButton Class="ma-1" OnClick=@Connect Color="primary">
|
||||
连接
|
||||
</MButton>
|
||||
|
||||
<MButton Class="ma-1" OnClick=@DisConnect Color="red">
|
||||
断开
|
||||
</MButton>
|
||||
</MRow>
|
||||
|
||||
|
||||
|
||||
</MCard>
|
||||
@@ -0,0 +1,94 @@
|
||||
#region copyright
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
#endregion
|
||||
|
||||
using System.Collections.Generic;
|
||||
|
||||
using ThingsGateway.Foundation.Adapter.OPCDA;
|
||||
using ThingsGateway.Foundation.Adapter.OPCDA.Da;
|
||||
|
||||
namespace ThingsGateway.Foundation.Demo;
|
||||
/// <summary>
|
||||
/// OPC
|
||||
/// </summary>
|
||||
public partial class OPCDAClientPage : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// 日志输出
|
||||
/// </summary>
|
||||
public Action<LogLevel, object, string, Exception> LogAction;
|
||||
|
||||
/// <summary>
|
||||
/// OPC
|
||||
/// </summary>
|
||||
public ThingsGateway.Foundation.Adapter.OPCDA.OPCDAClient OPC;
|
||||
|
||||
/// <summary>
|
||||
/// 日志输出
|
||||
/// </summary>
|
||||
public Action<List<ItemReadResult>> ValueAction;
|
||||
|
||||
private readonly OPCDANode node = new();
|
||||
|
||||
/// <summary>
|
||||
/// <inheritdoc/>
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
OPC.SafeDispose();
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
OPC = new ThingsGateway.Foundation.Adapter.OPCDA.OPCDAClient(LogOut);
|
||||
OPC.DataChangedHandler += Info_DataChangedHandler;
|
||||
OPC.Init(node);
|
||||
base.OnInitialized();
|
||||
}
|
||||
|
||||
private void Connect()
|
||||
{
|
||||
try
|
||||
{
|
||||
OPC.Disconnect();
|
||||
GetOPCClient().Connect();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogAction?.Invoke(LogLevel.Error, null, null, ex);
|
||||
}
|
||||
}
|
||||
private void DisConnect()
|
||||
{
|
||||
try
|
||||
{
|
||||
OPC.Disconnect();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogAction?.Invoke(LogLevel.Error, null, null, ex);
|
||||
}
|
||||
}
|
||||
|
||||
private ThingsGateway.Foundation.Adapter.OPCDA.OPCDAClient GetOPCClient()
|
||||
{
|
||||
//载入配置
|
||||
OPC.Init(node);
|
||||
return OPC;
|
||||
}
|
||||
|
||||
private void Info_DataChangedHandler(List<ItemReadResult> values)
|
||||
{
|
||||
ValueAction?.Invoke(values);
|
||||
}
|
||||
|
||||
private void LogOut(byte logLevel, object source, string message, Exception exception) => LogAction?.Invoke((LogLevel)logLevel, source, message, exception);
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
@*
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
*@
|
||||
|
||||
@namespace ThingsGateway.Foundation.Demo
|
||||
@using BlazorComponent;
|
||||
@using Microsoft.AspNetCore.Components.Web;
|
||||
@using System.Reflection;
|
||||
@inherits BaseComponentBase
|
||||
|
||||
@using ThingsGateway.Foundation.Adapter.OPCDA;
|
||||
@using ThingsGateway.Foundation.Core;
|
||||
@using ThingsGateway.Foundation.Adapter.OPCDA.Rcw;
|
||||
@using Masa.Blazor
|
||||
<MCard Class="ma-0">
|
||||
<MCardTitle Class="indigo white--text text-h6">
|
||||
@PLC?.OPCNode?.ToString()
|
||||
</MCardTitle>
|
||||
<MRow Class="pa-4"
|
||||
Justify="JustifyTypes.SpaceBetween">
|
||||
<MCol Cols="12" Md="6">
|
||||
<MTreeview @bind-Value=Selected @bind-Active=Actived SelectionType="SelectionType.Leaf"
|
||||
Items=Nodes TItem=OPCDATagModel
|
||||
Color="warning" Selectable Activatable OpenOnClick
|
||||
LoadChildren=PopulateBranchAsync
|
||||
TKey=BrowseElement ItemKey="r=>r.Tag"
|
||||
Style="height:500px;overflow-y:auto;"
|
||||
ItemText="r=>r.Name"
|
||||
ItemChildren="r=>r.Nodes">
|
||||
</MTreeview>
|
||||
</MCol>
|
||||
<MCol Cols="12" Md="6">
|
||||
<MCardText Style="height:500px;overflow-y:auto;">
|
||||
@if (Actived?.Count == 0)
|
||||
{
|
||||
<div key="title"
|
||||
class="text-h6 font-weight-light grey--text pa-4 text-center">
|
||||
选择左侧节点
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<ScrollXTransition>
|
||||
<MSheet Outlined Class="ma-0 pa-1">
|
||||
<MRow Align="AlignTypes.Center">
|
||||
<MCol>
|
||||
<MListItem>
|
||||
<ItemContent>
|
||||
<MListItemContent>
|
||||
<MListItemTitle>NodeId </MListItemTitle>
|
||||
</MListItemContent>
|
||||
</ItemContent>
|
||||
</MListItem>
|
||||
</MCol>
|
||||
<MDivider Vertical Center />
|
||||
<MCol>
|
||||
<MLabel Class=@($"ma-1")> @Actived.FirstOrDefault().ItemName </MLabel>
|
||||
</MCol>
|
||||
|
||||
</MRow>
|
||||
</MSheet>
|
||||
@if (nodeAttributes != null)
|
||||
{
|
||||
@foreach (var item in nodeAttributes)
|
||||
{
|
||||
<MSheet Outlined Class="ma-0 pa-1">
|
||||
<MRow Align="AlignTypes.Center">
|
||||
<MCol>
|
||||
<MListItem>
|
||||
<ItemContent>
|
||||
<MListItemContent>
|
||||
<MListItemTitle>@item.Description </MListItemTitle>
|
||||
</MListItemContent>
|
||||
</ItemContent>
|
||||
</MListItem>
|
||||
</MCol>
|
||||
<MDivider Vertical Center />
|
||||
<MCol>
|
||||
<MLabel Class=@($"{(item.ResultID.Failed()?"red--text":"green--text")} ma-1")> @item.Value </MLabel>
|
||||
</MCol>
|
||||
|
||||
</MRow>
|
||||
</MSheet>
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
</ScrollXTransition>
|
||||
|
||||
}
|
||||
</MCardText>
|
||||
</MCol>
|
||||
|
||||
<MDivider Vertical></MDivider>
|
||||
|
||||
</MRow>
|
||||
|
||||
|
||||
</MCard>
|
||||
<MOverlay Absolute Value="overlay" Opacity="0.8">
|
||||
<span class="green--text text--darken-2">
|
||||
@("正在获取OPCDA节点...")
|
||||
</span>
|
||||
<MProgressCircular Indeterminate Width=6 Size="30"></MProgressCircular>
|
||||
</MOverlay>
|
||||
@@ -0,0 +1,237 @@
|
||||
#region copyright
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
#endregion
|
||||
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
|
||||
using ThingsGateway.Foundation.Adapter.OPCDA.Rcw;
|
||||
#if Plugin
|
||||
using ThingsGateway.Plugin.OPCDA;
|
||||
#endif
|
||||
namespace ThingsGateway.Foundation.Demo;
|
||||
|
||||
/// <summary>
|
||||
/// 导入变量
|
||||
/// </summary>
|
||||
public partial class OPCDAImportVariable
|
||||
{
|
||||
private List<BrowseElement> actived = new();
|
||||
|
||||
private ItemProperty[] nodeAttributes;
|
||||
|
||||
private List<OPCDATagModel> Nodes = new();
|
||||
|
||||
private bool overlay = true;
|
||||
|
||||
/// <summary>
|
||||
/// opc对象
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public ThingsGateway.Foundation.Adapter.OPCDA.OPCDAClient PLC { get; set; }
|
||||
|
||||
private List<BrowseElement> Actived
|
||||
{
|
||||
get => actived;
|
||||
set
|
||||
{
|
||||
if (actived?.FirstOrDefault() != value?.FirstOrDefault() && value?.Count > 0)
|
||||
{
|
||||
actived = value;
|
||||
nodeAttributes = actived.FirstOrDefault().Properties;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private List<BrowseElement> Selected { get; set; } = new();
|
||||
|
||||
|
||||
#if Plugin
|
||||
|
||||
/// <summary>
|
||||
/// 获取设备与变量列表
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public (CollectDevice, List<DeviceVariable>) GetImportVariableList()
|
||||
{
|
||||
var device = GetImportDevice();
|
||||
//动态加载子项时,导出内容需要添加手动加载代码
|
||||
foreach (var node in Selected.ToList())
|
||||
{
|
||||
var nodes = PopulateBranch(node.ItemName, true);
|
||||
if (nodes.Count > 0)
|
||||
{
|
||||
Selected.AddRange(nodes.SelectMany(a => a.GetAllTags()).Select(a => a.Tag).Where(a => a != null).ToList());
|
||||
}
|
||||
}
|
||||
var data = Selected.Select(a =>
|
||||
{
|
||||
if (!a.IsItem || string.IsNullOrEmpty(a.ItemName))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
ProtectTypeEnum level = ProtectTypeEnum.ReadOnly;
|
||||
try
|
||||
{
|
||||
var userAccessLevel = (accessRights)(a.Properties.FirstOrDefault(b => b.ID.Code == 5).Value);
|
||||
level = userAccessLevel == accessRights.readable ? userAccessLevel == accessRights.writable ? ProtectTypeEnum.WriteOnly : ProtectTypeEnum.ReadOnly : ProtectTypeEnum.ReadWrite;
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
|
||||
var id = Yitter.IdGenerator.YitIdHelper.NextId();
|
||||
return new DeviceVariable()
|
||||
{
|
||||
Name = a.Name + "-" + id,
|
||||
Address = a.ItemName,
|
||||
DeviceId = device.Id,
|
||||
Id = id,
|
||||
ProtectTypeEnum = level,
|
||||
IntervalTime = 1000,
|
||||
RpcWriteEnable = true,
|
||||
};
|
||||
}).Where(a => a != null).ToList();
|
||||
return (device, data);
|
||||
}
|
||||
private CollectDevice GetImportDevice()
|
||||
{
|
||||
var id = Yitter.IdGenerator.YitIdHelper.NextId();
|
||||
var data = new CollectDevice()
|
||||
{
|
||||
Name = PLC.OPCNode.OPCName + "-" + id,
|
||||
Id = id,
|
||||
Enable = true,
|
||||
IsLogOut = true,
|
||||
DevicePropertys = new(),
|
||||
PluginName = "ThingsGateway.Plugin.OPCDA.OPCDAClient",
|
||||
};
|
||||
data.DevicePropertys.Add(new() { PropertyName = nameof(OPCDAClientProperty.OPCName), Value = PLC.OPCNode.OPCName, Description = typeof(OPCDAClientProperty).GetProperty(nameof(OPCDAClientProperty.OPCName)).GetCustomAttribute<DevicePropertyAttribute>().Description });
|
||||
data.DevicePropertys.Add(new() { PropertyName = nameof(OPCDAClientProperty.OPCIP), Value = PLC.OPCNode.OPCIP, Description = typeof(OPCDAClientProperty).GetProperty(nameof(OPCDAClientProperty.OPCIP)).GetCustomAttribute<DevicePropertyAttribute>().Description });
|
||||
data.DevicePropertys.Add(new() { PropertyName = nameof(OPCDAClientProperty.ActiveSubscribe), Value = PLC.OPCNode.ActiveSubscribe.ToString(), Description = typeof(OPCDAClientProperty).GetProperty(nameof(OPCDAClientProperty.ActiveSubscribe)).GetCustomAttribute<DevicePropertyAttribute>().Description });
|
||||
data.DevicePropertys.Add(new() { PropertyName = nameof(OPCDAClientProperty.CheckRate), Value = PLC.OPCNode.CheckRate.ToString(), Description = typeof(OPCDAClientProperty).GetProperty(nameof(OPCDAClientProperty.CheckRate)).GetCustomAttribute<DevicePropertyAttribute>().Description });
|
||||
data.DevicePropertys.Add(new() { PropertyName = nameof(OPCDAClientProperty.DeadBand), Value = PLC.OPCNode.DeadBand.ToString(), Description = typeof(OPCDAClientProperty).GetProperty(nameof(OPCDAClientProperty.DeadBand)).GetCustomAttribute<DevicePropertyAttribute>().Description });
|
||||
data.DevicePropertys.Add(new() { PropertyName = nameof(OPCDAClientProperty.GroupSize), Value = PLC.OPCNode.GroupSize.ToString(), Description = typeof(OPCDAClientProperty).GetProperty(nameof(OPCDAClientProperty.GroupSize)).GetCustomAttribute<DevicePropertyAttribute>().Description });
|
||||
data.DevicePropertys.Add(new() { PropertyName = nameof(OPCDAClientProperty.UpdateRate), Value = PLC.OPCNode.UpdateRate.ToString(), Description = typeof(OPCDAClientProperty).GetProperty(nameof(OPCDAClientProperty.UpdateRate)).GetCustomAttribute<DevicePropertyAttribute>().Description });
|
||||
return data;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
await Task.Factory.StartNew(async () =>
|
||||
{
|
||||
Nodes = PopulateBranch("");
|
||||
overlay = false;
|
||||
await InvokeAsync(StateHasChanged);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
private List<OPCDATagModel> PopulateBranch(string sourceId, bool isAll = false)
|
||||
{
|
||||
List<OPCDATagModel> nodes = new()
|
||||
{
|
||||
new OPCDATagModel() { Name = "Browsering..." }
|
||||
};
|
||||
try
|
||||
{
|
||||
var references = PLC.GetBrowseElements(sourceId);
|
||||
List<OPCDATagModel> list = new();
|
||||
if (references != null)
|
||||
{
|
||||
for (int ii = 0; ii < references.Count; ii++)
|
||||
{
|
||||
var target = references[ii];
|
||||
OPCDATagModel child = new()
|
||||
{
|
||||
Name = target.Name,
|
||||
Tag = target
|
||||
};
|
||||
|
||||
if (target.HasChildren)
|
||||
{
|
||||
if (isAll)
|
||||
child.Nodes = PopulateBranch(target.ItemName);
|
||||
else
|
||||
child.Nodes = new();
|
||||
}
|
||||
else
|
||||
{
|
||||
child.Nodes = null;
|
||||
}
|
||||
|
||||
list.Add(child);
|
||||
}
|
||||
}
|
||||
|
||||
List<OPCDATagModel> listNode = list;
|
||||
nodes.Clear();
|
||||
nodes.AddRange(listNode.ToArray());
|
||||
return nodes;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return new()
|
||||
{
|
||||
new()
|
||||
{
|
||||
Name = ex.Message,
|
||||
Tag = new(),
|
||||
Nodes = null
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
private async Task PopulateBranchAsync(OPCDATagModel model)
|
||||
{
|
||||
await Task.Run(() =>
|
||||
{
|
||||
var sourceId = model.Tag.ItemName;
|
||||
model.Nodes = PopulateBranch(sourceId);
|
||||
});
|
||||
}
|
||||
internal class OPCDATagModel
|
||||
{
|
||||
internal string Name { get; set; }
|
||||
internal string NodeId => (Tag?.ItemName)?.ToString();
|
||||
internal List<OPCDATagModel> Nodes { get; set; } = new();
|
||||
internal BrowseElement Tag { get; set; }
|
||||
public List<OPCDATagModel> GetAllTags()
|
||||
{
|
||||
List<OPCDATagModel> allTags = new();
|
||||
GetAllTagsRecursive(this, allTags);
|
||||
return allTags;
|
||||
}
|
||||
|
||||
private void GetAllTagsRecursive(OPCDATagModel parentTag, List<OPCDATagModel> allTags)
|
||||
{
|
||||
allTags.Add(parentTag);
|
||||
if (parentTag.Nodes != null)
|
||||
foreach (OPCDATagModel childTag in parentTag.Nodes)
|
||||
{
|
||||
GetAllTagsRecursive(childTag, allTags);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
@*
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
*@
|
||||
|
||||
@page "/OPCUAClient"
|
||||
|
||||
|
||||
@namespace ThingsGateway.Foundation.Demo
|
||||
|
||||
@using BlazorComponent;
|
||||
@using Microsoft.AspNetCore.Components.Web;
|
||||
@using Masa.Blazor.Presets;
|
||||
@using Microsoft.JSInterop;
|
||||
@inherits BaseComponentBase
|
||||
|
||||
|
||||
@using ThingsGateway.Foundation.Core;
|
||||
@using ThingsGateway.Foundation.Adapter.OPCUA;
|
||||
@using ThingsGateway.Foundation.Extension;
|
||||
@using ThingsGateway.Foundation.Serial;
|
||||
@using Masa.Blazor
|
||||
|
||||
|
||||
<OPCUAClientPage @ref=opcUAClientPage></OPCUAClientPage>
|
||||
|
||||
|
||||
|
||||
<DriverDebugUIPage @ref=_driverDebugUIPage Sections="_sections" ShowDefaultOtherContent=false>
|
||||
<ReadWriteContent>
|
||||
<div class="my-1 py-1">
|
||||
|
||||
<MTextarea Class="mx-1 my-1" Label="变量地址" Dense Outlined HideDetails="@("auto")" @bind-Value=@_driverDebugUIPage.Address />
|
||||
|
||||
<MButton Class="mx-1 my-1" Color="primary" OnClick="Add">
|
||||
添加
|
||||
</MButton>
|
||||
<MButton Class="mx-1 my-1" Color="primary" OnClick="Remove">
|
||||
移除
|
||||
</MButton>
|
||||
<MButton Class="mx-1 my-1" Color="primary" OnClick="ReadAsync">
|
||||
组读取
|
||||
</MButton>
|
||||
|
||||
<MTextarea Class="mx-1 mt-3 my-1" Label="值" Dense Outlined HideDetails="@("auto")" @bind-Value=@_driverDebugUIPage.WriteValue />
|
||||
<MButton Class="mx-1 my-1" Color="primary" OnClick="WriteAsync">
|
||||
写入
|
||||
</MButton>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
<div class="my-1 ml-2">
|
||||
|
||||
<MRow Align="AlignTypes.Center" >
|
||||
|
||||
<MCheckbox HideDetails="@("auto")" @bind-Value=IsShowSubvariable Dense Label="是否显示子变量"/>
|
||||
|
||||
<MButton Class="mx-1" Color="accent" OnClick="()=>IsShowImportVariableList=!IsShowImportVariableList">
|
||||
<span>查看OPC节点空间</span>
|
||||
</MButton>
|
||||
</MRow>
|
||||
</div>
|
||||
|
||||
<MCol Class="my-1 py-1">
|
||||
<MRow Align="AlignTypes.Center">
|
||||
|
||||
|
||||
|
||||
</MRow>
|
||||
|
||||
</MCol>
|
||||
|
||||
</ReadWriteContent>
|
||||
</DriverDebugUIPage>
|
||||
|
||||
|
||||
@{
|
||||
#if Plugin
|
||||
<PModal @bind-Value="IsShowImportVariableList" OnCancel="() => IsShowImportVariableList = false"
|
||||
Title=导入OPC变量 Height=@("700") Persistent
|
||||
MaxWidth="1500" OnSave="DownDeviceExport">
|
||||
<SaveContent Context="save">
|
||||
|
||||
<MMenu OffsetY Context="menu">
|
||||
<ActivatorContent>
|
||||
<MButton @attributes="@menu.Attrs" Color="primary" Class="my-1 mx-2 " Disabled="isDownLoading" Loading="isDownLoading">
|
||||
导出
|
||||
<AppChevronDown></AppChevronDown>
|
||||
</MButton>
|
||||
</ActivatorContent>
|
||||
<ChildContent>
|
||||
<MList>
|
||||
<MListItem Color="primary" OnClick="DownDeviceExport" Disabled="isDownLoading" Loading="isDownLoading"> 导出到excel </MListItem>
|
||||
<MListItem Color="primary" OnClick="DeviceImport" Disabled="isDownLoading" Loading="isDownLoading">导入到系统</MListItem>
|
||||
</MList>
|
||||
|
||||
</ChildContent>
|
||||
</MMenu>
|
||||
|
||||
</SaveContent>
|
||||
<ChildContent>
|
||||
@if (IsShowImportVariableList)
|
||||
{
|
||||
<OPCUAImportVariable @ref=ImportVariable PLC="_plc"></OPCUAImportVariable>
|
||||
}
|
||||
</ChildContent>
|
||||
|
||||
|
||||
</PModal>
|
||||
|
||||
#else
|
||||
<PModal @bind-Value="IsShowImportVariableList" OnCancel="() => IsShowImportVariableList = false"
|
||||
Title=导入OPC变量 Height=@("700") Persistent
|
||||
MaxWidth="1500">
|
||||
<ChildContent>
|
||||
@if (IsShowImportVariableList)
|
||||
{
|
||||
<OPCUAImportVariable @ref=ImportVariable PLC="_plc" IsShowSubvariable=IsShowSubvariable></OPCUAImportVariable>
|
||||
}
|
||||
</ChildContent>
|
||||
|
||||
|
||||
</PModal>
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@code {
|
||||
private readonly List<(string Code, string Language)> _sections = new();
|
||||
private bool IsShowSubvariable;
|
||||
}
|
||||
@@ -0,0 +1,239 @@
|
||||
#region copyright
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
#endregion
|
||||
|
||||
using BlazorComponent;
|
||||
|
||||
using Masa.Blazor;
|
||||
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.JSInterop;
|
||||
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
using Opc.Ua;
|
||||
|
||||
using ThingsGateway.Foundation.Adapter.OPCUA;
|
||||
|
||||
namespace ThingsGateway.Foundation.Demo;
|
||||
/// <summary>
|
||||
/// OPCUA调试页面
|
||||
/// </summary>
|
||||
public partial class OPCUAClientDebugPage
|
||||
{
|
||||
private ThingsGateway.Foundation.Adapter.OPCUA.OPCUAClient _plc;
|
||||
private DriverDebugUIPage _driverDebugUIPage;
|
||||
bool IsShowImportVariableList;
|
||||
private OPCUAClientPage opcUAClientPage;
|
||||
private OPCUAImportVariable ImportVariable { get; set; }
|
||||
[Inject]
|
||||
private InitTimezone InitTimezone { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// <inheritdoc/>
|
||||
/// </summary>
|
||||
public override void Dispose()
|
||||
{
|
||||
_plc.SafeDispose();
|
||||
opcUAClientPage.SafeDispose();
|
||||
base.Dispose();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string ToString()
|
||||
{
|
||||
return nameof(OPCUAClient);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void OnAfterRender(bool firstRender)
|
||||
{
|
||||
if (firstRender)
|
||||
{
|
||||
if (opcUAClientPage != null)
|
||||
{
|
||||
opcUAClientPage.LogAction = _driverDebugUIPage.LogOut;
|
||||
_plc = opcUAClientPage.OPC;
|
||||
_plc.DataChangedHandler += Plc_DataChangedHandler;
|
||||
}
|
||||
//载入配置
|
||||
StateHasChanged();
|
||||
_driverDebugUIPage.Sections.Clear();
|
||||
|
||||
}
|
||||
|
||||
base.OnAfterRender(firstRender);
|
||||
}
|
||||
private async Task Add()
|
||||
{
|
||||
if (_plc.Connected)
|
||||
await _plc.AddSubscriptionAsync(Guid.NewGuid().ToString(), new[] { _driverDebugUIPage.Address });
|
||||
else
|
||||
{
|
||||
_driverDebugUIPage.Messages.Add((Microsoft.Extensions.Logging.LogLevel.Debug, $"{DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(InitTimezone.TimezoneOffset)} - 未连接"));
|
||||
}
|
||||
}
|
||||
|
||||
#if Plugin
|
||||
private async Task DeviceImport()
|
||||
{
|
||||
isDownLoading = true;
|
||||
await InvokeAsync(StateHasChanged);
|
||||
try
|
||||
{
|
||||
var data = await ImportVariable?.GetImportVariableListAsync();
|
||||
if (data.Item2?.Count == 0)
|
||||
{
|
||||
await PopupService.EnqueueSnackbarAsync("无可用变量", AlertTypes.Warning);
|
||||
return;
|
||||
}
|
||||
await _serviceScope.ServiceProvider.GetService<CollectDeviceService>().AddAsync(data.Item1);
|
||||
await _serviceScope.ServiceProvider.GetService<VariableService>().AddBatchAsync(data.Item2);
|
||||
await PopupService.EnqueueSnackbarAsync("成功", AlertTypes.Success);
|
||||
}
|
||||
finally
|
||||
{
|
||||
isDownLoading = false;
|
||||
}
|
||||
|
||||
}
|
||||
private IJSObjectReference JSObjectReference;
|
||||
private bool isDownLoading;
|
||||
/// <inheritdoc/>
|
||||
[Inject]
|
||||
protected IJSRuntime JSRuntime { get; set; }
|
||||
private async Task DownDeviceExport()
|
||||
{
|
||||
isDownLoading = true;
|
||||
await InvokeAsync(StateHasChanged);
|
||||
try
|
||||
{
|
||||
var data = await ImportVariable?.GetImportVariableListAsync();
|
||||
if (data.Item2?.Count == 0)
|
||||
{
|
||||
await PopupService.EnqueueSnackbarAsync("无可用变量", AlertTypes.Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
await DownDeviceExportAsync(data.Item1);
|
||||
await DownDeviceVariableExportAsync(data.Item2, data.Item1.Name);
|
||||
await PopupService.EnqueueSnackbarAsync("成功", AlertTypes.Success);
|
||||
}
|
||||
finally
|
||||
{
|
||||
isDownLoading = false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 导出到excel
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public async Task DownDeviceExportAsync(CollectDevice data)
|
||||
{
|
||||
using var memoryStream = await _serviceScope.ServiceProvider.GetService<CollectDeviceService>().ExportFileAsync(new List<CollectDevice>() { data });
|
||||
using var streamRef = new DotNetStreamReference(stream: memoryStream);
|
||||
JSObjectReference ??= await JSRuntime.LoadModuleAsync("js/downloadFileFromStream");
|
||||
await JSObjectReference.InvokeVoidAsync("downloadFileFromStream", $"设备导出{DateTimeExtensions.CurrentDateTime.ToFileDateTimeFormat()}.xlsx", streamRef);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 导出到excel
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public async Task DownDeviceVariableExportAsync(List<DeviceVariable> data, string devName)
|
||||
{
|
||||
using var memoryStream = await _serviceScope.ServiceProvider.GetService<VariableService>().ExportFileAsync(data, devName);
|
||||
using var streamRef = new DotNetStreamReference(stream: memoryStream);
|
||||
JSObjectReference ??= await JSRuntime.LoadModuleAsync("js/downloadFileFromStream");
|
||||
await JSObjectReference.InvokeVoidAsync("downloadFileFromStream", $"变量导出{DateTimeExtensions.CurrentDateTime.ToFileDateTimeFormat()}.xlsx", streamRef);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
private void Plc_DataChangedHandler((VariableNode variableNode, DataValue dataValue, Newtonsoft.Json.Linq.JToken jToken) item)
|
||||
{
|
||||
_driverDebugUIPage.Messages.Add((Microsoft.Extensions.Logging.LogLevel.Debug,
|
||||
$"{DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(InitTimezone.TimezoneOffset)} - {item.variableNode.NodeId}:{item.jToken}"));
|
||||
if (_driverDebugUIPage.Messages.Count > 2500)
|
||||
{
|
||||
_driverDebugUIPage.Messages.Clear();
|
||||
}
|
||||
|
||||
}
|
||||
private async Task ReadAsync()
|
||||
{
|
||||
if (_plc.Connected)
|
||||
{
|
||||
try
|
||||
{
|
||||
var data = await _plc.ReadJTokenValueAsync(new string[] { _driverDebugUIPage.Address });
|
||||
_driverDebugUIPage.Messages.Add((Microsoft.Extensions.Logging.LogLevel.Debug,
|
||||
$"{DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(InitTimezone.TimezoneOffset)} - {data[0].Item1}:{data[0].Item3}"));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
_driverDebugUIPage.Messages.Add((Microsoft.Extensions.Logging.LogLevel.Warning,
|
||||
$"{DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(InitTimezone.TimezoneOffset)} - {ex}"));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_driverDebugUIPage.Messages.Add((Microsoft.Extensions.Logging.LogLevel.Warning,
|
||||
$"{DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(InitTimezone.TimezoneOffset)} - 未连接"));
|
||||
}
|
||||
}
|
||||
private void Remove()
|
||||
{
|
||||
if (_plc.Connected)
|
||||
_plc.RemoveSubscription("");
|
||||
else
|
||||
{
|
||||
_driverDebugUIPage.Messages.Add((Microsoft.Extensions.Logging.LogLevel.Warning,
|
||||
$"{DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(InitTimezone.TimezoneOffset)} - 未连接"));
|
||||
}
|
||||
}
|
||||
|
||||
private async Task WriteAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_plc.Connected)
|
||||
{
|
||||
var data = await _plc.WriteNodeAsync(
|
||||
new()
|
||||
{
|
||||
{ _driverDebugUIPage.Address, JToken.Parse(_driverDebugUIPage.WriteValue)}
|
||||
}
|
||||
);
|
||||
|
||||
foreach (var item in data)
|
||||
{
|
||||
_driverDebugUIPage.Messages.Add((item.Value.Item1 ? Microsoft.Extensions.Logging.LogLevel.Warning : Microsoft.Extensions.Logging.LogLevel.Information,
|
||||
$"{DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(InitTimezone.TimezoneOffset)} - {item.Value.Item2}"));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_driverDebugUIPage.Messages.Add((Microsoft.Extensions.Logging.LogLevel.Warning,
|
||||
$"{DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(InitTimezone.TimezoneOffset)} - 未连接"));
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_driverDebugUIPage.Messages.Add((Microsoft.Extensions.Logging.LogLevel.Error,
|
||||
$"{DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(InitTimezone.TimezoneOffset)} - {ex}"));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
@*
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
*@
|
||||
|
||||
@namespace ThingsGateway.Foundation.Demo
|
||||
|
||||
@using BlazorComponent;
|
||||
@using Microsoft.AspNetCore.Components.Web;
|
||||
@using System.IO.Ports;
|
||||
@using System.Collections.Concurrent;
|
||||
@using Opc.Ua.Client;
|
||||
@using Opc.Ua;
|
||||
@using System.Linq;
|
||||
@using ThingsGateway.Foundation.Core;
|
||||
@using ThingsGateway.Foundation.Adapter.OPCUA;
|
||||
@using Masa.Blazor
|
||||
@implements IDisposable
|
||||
|
||||
<MCard Flat Class="pa-2 my-1" Style="width:100%">
|
||||
<div class="mb-4">设备配置</div>
|
||||
<MRow Justify="JustifyTypes.Start" Align="AlignTypes.Center">
|
||||
<MTextField Class="ma-1" Label=@node.Description(a=>a.GroupSize) Dense Outlined HideDetails="@("auto")" @bind-Value=@node.GroupSize />
|
||||
<MTextField Class="ma-1" Label=@node.Description(a=>a.UpdateRate) Dense Outlined HideDetails="@("auto")" @bind-Value=@node.UpdateRate />
|
||||
|
||||
<MTextField Class="ma-1" Label=@node.Description(a=>a.DeadBand) Dense Outlined HideDetails="@("auto")" @bind-Value=@node.DeadBand />
|
||||
<MTextField Class="ma-1" Label=@node.Description(a=>a.KeepAliveInterval) Dense Outlined HideDetails="@("auto")" @bind-Value=@node.KeepAliveInterval />
|
||||
|
||||
<MTextField Class="ma-1" Label=@node.Description(a=>a.OPCUrl) Dense Outlined HideDetails="@("auto")" @bind-Value=@node.OPCUrl />
|
||||
<MTextField Class="ma-1" Label=@node.Description(a=>a.UserName) Dense Outlined HideDetails="@("auto")" @bind-Value=@node.UserName />
|
||||
<MTextField Class="ma-1" Label=@node.Description(a=>a.Password) Dense Outlined HideDetails="@("auto")" @bind-Value=@node.Password />
|
||||
|
||||
<MCheckbox Class="ma-1" Label=@node.Description(a=>a.ActiveSubscribe) Dense HideDetails="@("auto")" @bind-Value=@node.ActiveSubscribe />
|
||||
<MCheckbox Class="ma-1" Label=@node.Description(a=>a.IsUseSecurity) Dense HideDetails="@("auto")" @bind-Value=@node.IsUseSecurity />
|
||||
<MCheckbox Class="ma-1" Label=@node.Description(a=>a.CheckDomain) Dense HideDetails="@("auto")" @bind-Value=@node.CheckDomain />
|
||||
<MCheckbox Class="ma-1" Label=@node.Description(a=>a.LoadType) Dense HideDetails="@("auto")" @bind-Value=@node.LoadType />
|
||||
|
||||
<MButton Class="ma-1" OnClick=@ConnectAsync Color="primary">
|
||||
连接
|
||||
</MButton>
|
||||
|
||||
<MButton Class="ma-1" OnClick=@DisConnect Color="red">
|
||||
断开
|
||||
</MButton>
|
||||
</MRow>
|
||||
|
||||
|
||||
|
||||
</MCard>
|
||||
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
#region copyright
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
#endregion
|
||||
|
||||
using ThingsGateway.Foundation.Adapter.OPCUA;
|
||||
|
||||
namespace ThingsGateway.Foundation.Demo;
|
||||
|
||||
/// <summary>
|
||||
/// OPCUAClientPage
|
||||
/// </summary>
|
||||
public partial class OPCUAClientPage : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// 日志输出
|
||||
/// </summary>
|
||||
public Action<LogLevel, object, string, Exception> LogAction;
|
||||
/// <summary>
|
||||
/// OPC
|
||||
/// </summary>
|
||||
public ThingsGateway.Foundation.Adapter.OPCUA.OPCUAClient OPC;
|
||||
|
||||
private readonly OPCUANode node = new();
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// <inheritdoc/>
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
OPC.OpcStatusChange -= OPC_OpcStatusChange;
|
||||
OPC.SafeDispose();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
OPC = new ThingsGateway.Foundation.Adapter.OPCUA.OPCUAClient();
|
||||
OPC.OpcStatusChange += OPC_OpcStatusChange;
|
||||
base.OnInitialized();
|
||||
}
|
||||
|
||||
private void OPC_OpcStatusChange(object sender, OpcUaStatusEventArgs e)
|
||||
{
|
||||
if (e.LogLevel > 0)
|
||||
LogAction?.Invoke((LogLevel)e.LogLevel, null, e.Text, null);
|
||||
}
|
||||
|
||||
private async Task ConnectAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
OPC.Disconnect();
|
||||
await GetOPCClient().ConnectAsync(CancellationToken.None);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogAction?.Invoke(LogLevel.Error, null, null, ex);
|
||||
}
|
||||
}
|
||||
private void DisConnect()
|
||||
{
|
||||
try
|
||||
{
|
||||
OPC.Disconnect();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogAction?.Invoke(LogLevel.Error, null, null, ex);
|
||||
}
|
||||
}
|
||||
private ThingsGateway.Foundation.Adapter.OPCUA.OPCUAClient GetOPCClient()
|
||||
{
|
||||
OPC.OPCNode = node;
|
||||
return OPC;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
@*
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
*@
|
||||
|
||||
@namespace ThingsGateway.Foundation.Demo
|
||||
@inherits BaseComponentBase
|
||||
|
||||
@using BlazorComponent;
|
||||
@using Microsoft.AspNetCore.Components.Web;
|
||||
@using Opc.Ua;
|
||||
@using System.Reflection;
|
||||
@using ThingsGateway.Foundation.Adapter.OPCUA;
|
||||
@using ThingsGateway.Foundation.Core;
|
||||
@using Masa.Blazor
|
||||
|
||||
<MCard Class="ma-0">
|
||||
<MCardTitle Class="indigo white--text text-h6">
|
||||
@PLC?.OPCNode?.OPCUrl
|
||||
</MCardTitle>
|
||||
<MRow Class="pa-4"
|
||||
Justify="JustifyTypes.SpaceBetween">
|
||||
<MCol Cols="12" Md="6">
|
||||
<MTreeview @bind-Value=Selected @bind-Active=Actived SelectionType="SelectionType.Leaf"
|
||||
Items=Nodes TItem=OPCUATagModel
|
||||
Color="warning" Selectable Activatable OpenOnClick
|
||||
LoadChildren=PopulateBranchAsync
|
||||
TKey=ReferenceDescription ItemKey="r=>r.Tag"
|
||||
Style="height:500px;overflow-y:auto;"
|
||||
ItemText="r=>r.Name"
|
||||
ItemChildren="r=>r.Nodes">
|
||||
|
||||
</MTreeview>
|
||||
|
||||
</MCol>
|
||||
<MCol Cols="12" Md="6">
|
||||
<MCardText Style="height:500px;overflow-y:auto;">
|
||||
@if (Actived?.Count == 0 || Actived?.FirstOrDefault()?.NodeId==null)
|
||||
{
|
||||
<div key="title"
|
||||
class="text-h6 font-weight-light grey--text pa-4 text-center">
|
||||
选择左侧节点
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<ScrollXTransition>
|
||||
<MSheet Outlined Class="ma-0 pa-1">
|
||||
<MRow Align="AlignTypes.Center">
|
||||
<MCol>
|
||||
<MListItem>
|
||||
<ItemContent>
|
||||
<MListItemContent>
|
||||
<MListItemTitle>NodeId </MListItemTitle>
|
||||
</MListItemContent>
|
||||
</ItemContent>
|
||||
</MListItem>
|
||||
</MCol>
|
||||
<MDivider Vertical Center />
|
||||
<MCol>
|
||||
<MLabel Class=@($"ma-1")> @Actived.FirstOrDefault().NodeId </MLabel>
|
||||
</MCol>
|
||||
|
||||
</MRow>
|
||||
</MSheet>
|
||||
@foreach (var item in nodeAttributes)
|
||||
{
|
||||
<MSheet Outlined Class="ma-0 pa-1">
|
||||
<MRow Align="AlignTypes.Center">
|
||||
<MCol>
|
||||
<MListItem>
|
||||
<ItemContent>
|
||||
<MListItemContent>
|
||||
<MListItemTitle>@item.Name </MListItemTitle>
|
||||
</MListItemContent>
|
||||
</ItemContent>
|
||||
</MListItem>
|
||||
</MCol>
|
||||
<MDivider Vertical Center />
|
||||
<MCol>
|
||||
<MLabel Class=@($"{(StatusCode.IsBad(item.StatusCode)?"red--text":"green--text")} ma-1")> @item.Value </MLabel>
|
||||
</MCol>
|
||||
|
||||
</MRow>
|
||||
</MSheet>
|
||||
}
|
||||
|
||||
</ScrollXTransition>
|
||||
|
||||
}
|
||||
</MCardText>
|
||||
</MCol>
|
||||
|
||||
<MDivider Vertical></MDivider>
|
||||
|
||||
</MRow>
|
||||
|
||||
</MCard>
|
||||
<MOverlay Absolute Value="overlay" Opacity="0.8">
|
||||
<span class="green--text text--darken-2">
|
||||
@("正在获取OPCUA节点...")
|
||||
</span>
|
||||
<MProgressCircular Indeterminate Width=6
|
||||
Size="30"></MProgressCircular>
|
||||
</MOverlay>
|
||||
@@ -0,0 +1,320 @@
|
||||
#region copyright
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
#endregion
|
||||
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
using Opc.Ua;
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
|
||||
using ThingsGateway.Foundation.Adapter.OPCUA;
|
||||
|
||||
#if Plugin
|
||||
using ThingsGateway.Plugin.OPCUA;
|
||||
#endif
|
||||
namespace ThingsGateway.Foundation.Demo;
|
||||
|
||||
/// <summary>
|
||||
/// 导入变量
|
||||
/// </summary>
|
||||
public partial class OPCUAImportVariable
|
||||
{
|
||||
private List<ReferenceDescription> actived = new();
|
||||
|
||||
private OPCNodeAttribute[] nodeAttributes;
|
||||
|
||||
private List<OPCUATagModel> Nodes = new();
|
||||
|
||||
private bool overlay = true;
|
||||
/// <summary>
|
||||
/// opc对象
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public ThingsGateway.Foundation.Adapter.OPCUA.OPCUAClient PLC { get; set; }
|
||||
/// <summary>
|
||||
/// 是否显示子变量
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public bool IsShowSubvariable { get; set; }
|
||||
|
||||
private List<ReferenceDescription> Actived
|
||||
{
|
||||
get => actived;
|
||||
set
|
||||
{
|
||||
if (actived?.FirstOrDefault() != value?.FirstOrDefault() && value?.Count > 0)
|
||||
{
|
||||
actived = value;
|
||||
if (actived.FirstOrDefault().NodeId != null)
|
||||
nodeAttributes = PLC.ReadNoteAttributes(actived.FirstOrDefault().NodeId.ToString());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private List<ReferenceDescription> Selected { get; set; } = new();
|
||||
|
||||
#if Plugin
|
||||
/// <summary>
|
||||
/// 获取设备与变量列表
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public async Task<(CollectDevice, List<DeviceVariable>)> GetImportVariableListAsync()
|
||||
{
|
||||
var device = GetImportDevice();
|
||||
//动态加载子项时,导出内容需要添加手动加载代码
|
||||
foreach (var node in Selected.ToList())
|
||||
{
|
||||
List<OPCUATagModel> nodes = await PopulateBranchAsync((NodeId)node.NodeId, true, IsShowSubvariable);
|
||||
if (nodes.Count > 0)
|
||||
{
|
||||
Selected.AddRange(nodes.SelectMany(a => a.GetAllTags()).Select(a => a.Tag).Where(a => a != null).ToList());
|
||||
}
|
||||
}
|
||||
var data = (await SelectAsync(Selected, async a =>
|
||||
{
|
||||
var nodeClass = (await PLC.ReadNoteAttributeAsync(a.NodeId.ToString(), Attributes.NodeClass)).FirstOrDefault().Value.ToString();
|
||||
if (nodeClass == nameof(NodeClass.Variable))
|
||||
{
|
||||
ProtectTypeEnum level = ProtectTypeEnum.ReadOnly;
|
||||
DataTypeEnum dataTypeEnum = DataTypeEnum.Object;
|
||||
try
|
||||
{
|
||||
var userAccessLevel = (AccessLevelType)(await PLC.ReadNoteAttributeAsync(a.NodeId.ToString(), Attributes.UserAccessLevel)).FirstOrDefault().Value;
|
||||
level = (userAccessLevel.HasFlag(AccessLevelType.CurrentRead)) ?
|
||||
userAccessLevel.HasFlag(AccessLevelType.CurrentWrite) ?
|
||||
ProtectTypeEnum.ReadWrite : ProtectTypeEnum.ReadOnly : ProtectTypeEnum.WriteOnly;
|
||||
|
||||
var dataTypeId = (Opc.Ua.NodeId)(await PLC.ReadNoteAttributeAsync(a.NodeId.ToString(), Attributes.DataType)).FirstOrDefault().Value;
|
||||
var dataType = Opc.Ua.TypeInfo.GetSystemType(dataTypeId, PLC.Session.Factory);
|
||||
var result = dataType != null && Enum.TryParse<DataTypeEnum>(dataType.Name, out dataTypeEnum);
|
||||
if (!result)
|
||||
{
|
||||
dataTypeEnum = DataTypeEnum.Object;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
var id = Yitter.IdGenerator.YitIdHelper.NextId();
|
||||
|
||||
return new DeviceVariable()
|
||||
{
|
||||
Name = a.DisplayName.Text + "-" + id,
|
||||
Address = a.NodeId.ToString(),
|
||||
DeviceId = device.Id,
|
||||
DataTypeEnum = dataTypeEnum,
|
||||
Id = id,
|
||||
ProtectTypeEnum = level,
|
||||
IntervalTime = 1000,
|
||||
RpcWriteEnable = true,
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
})).Where(a => a != null).ToList();
|
||||
return (device, data);
|
||||
}
|
||||
|
||||
|
||||
/// <inheritdoc/>
|
||||
private CollectDevice GetImportDevice()
|
||||
{
|
||||
var id = Yitter.IdGenerator.YitIdHelper.NextId();
|
||||
var data = new CollectDevice()
|
||||
{
|
||||
Name = PLC.OPCNode.OPCUrl + "-" + id,
|
||||
Id = id,
|
||||
Enable = true,
|
||||
IsLogOut = true,
|
||||
DevicePropertys = new(),
|
||||
PluginName = "ThingsGateway.Plugin.OPCUA.OPCUAClient",
|
||||
};
|
||||
|
||||
data.DevicePropertys.Add(new() { PropertyName = nameof(OPCUAClientProperty.OPCURL), Value = PLC.OPCNode.OPCUrl, Description = typeof(OPCUAClientProperty).GetProperty(nameof(OPCUAClientProperty.OPCURL)).GetCustomAttribute<DevicePropertyAttribute>().Description });
|
||||
data.DevicePropertys.Add(new() { PropertyName = nameof(OPCUAClientProperty.UserName), Value = PLC.OPCNode.UserName, Description = typeof(OPCUAClientProperty).GetProperty(nameof(OPCUAClientProperty.UserName)).GetCustomAttribute<DevicePropertyAttribute>().Description });
|
||||
data.DevicePropertys.Add(new() { PropertyName = nameof(OPCUAClientProperty.Password), Value = PLC.OPCNode.Password, Description = typeof(OPCUAClientProperty).GetProperty(nameof(OPCUAClientProperty.Password)).GetCustomAttribute<DevicePropertyAttribute>().Description });
|
||||
data.DevicePropertys.Add(new() { PropertyName = nameof(OPCUAClientProperty.CheckDomain), Value = PLC.OPCNode.CheckDomain.ToString(), Description = typeof(OPCUAClientProperty).GetProperty(nameof(OPCUAClientProperty.CheckDomain)).GetCustomAttribute<DevicePropertyAttribute>().Description });
|
||||
data.DevicePropertys.Add(new() { PropertyName = nameof(OPCUAClientProperty.LoadType), Value = PLC.OPCNode.LoadType.ToString(), Description = typeof(OPCUAClientProperty).GetProperty(nameof(OPCUAClientProperty.LoadType)).GetCustomAttribute<DevicePropertyAttribute>().Description });
|
||||
data.DevicePropertys.Add(new() { PropertyName = nameof(OPCUAClientProperty.IsUseSecurity), Value = PLC.OPCNode.IsUseSecurity.ToString(), Description = typeof(OPCUAClientProperty).GetProperty(nameof(OPCUAClientProperty.IsUseSecurity)).GetCustomAttribute<DevicePropertyAttribute>().Description });
|
||||
data.DevicePropertys.Add(new() { PropertyName = nameof(OPCUAClientProperty.ActiveSubscribe), Value = true.ToString(), Description = typeof(OPCUAClientProperty).GetProperty(nameof(OPCUAClientProperty.ActiveSubscribe)).GetCustomAttribute<DevicePropertyAttribute>().Description });
|
||||
data.DevicePropertys.Add(new() { PropertyName = nameof(OPCUAClientProperty.DeadBand), Value = PLC.OPCNode.DeadBand.ToString(), Description = typeof(OPCUAClientProperty).GetProperty(nameof(OPCUAClientProperty.DeadBand)).GetCustomAttribute<DevicePropertyAttribute>().Description });
|
||||
data.DevicePropertys.Add(new() { PropertyName = nameof(OPCUAClientProperty.GroupSize), Value = PLC.OPCNode.GroupSize.ToString(), Description = typeof(OPCUAClientProperty).GetProperty(nameof(OPCUAClientProperty.GroupSize)).GetCustomAttribute<DevicePropertyAttribute>().Description });
|
||||
data.DevicePropertys.Add(new() { PropertyName = nameof(OPCUAClientProperty.UpdateRate), Value = PLC.OPCNode.UpdateRate.ToString(), Description = typeof(OPCUAClientProperty).GetProperty(nameof(OPCUAClientProperty.UpdateRate)).GetCustomAttribute<DevicePropertyAttribute>().Description });
|
||||
data.DevicePropertys.Add(new() { PropertyName = nameof(OPCUAClientProperty.KeepAliveInterval), Value = PLC.OPCNode.KeepAliveInterval.ToString(), Description = typeof(OPCUAClientProperty).GetProperty(nameof(OPCUAClientProperty.KeepAliveInterval)).GetCustomAttribute<DevicePropertyAttribute>().Description });
|
||||
return data;
|
||||
}
|
||||
|
||||
#endif
|
||||
/// <summary>
|
||||
/// <inheritdoc/>
|
||||
/// </summary>
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
Task.Run(async () =>
|
||||
{
|
||||
Nodes = await PopulateBranchAsync(ObjectIds.ObjectsFolder, isShowSubvariable: IsShowSubvariable);
|
||||
overlay = false;
|
||||
await InvokeAsync(StateHasChanged);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
private async Task<ReferenceDescriptionCollection> GetReferenceDescriptionCollectionAsync(NodeId sourceId)
|
||||
{
|
||||
BrowseDescription nodeToBrowse1 = new()
|
||||
{
|
||||
NodeId = sourceId,
|
||||
BrowseDirection = BrowseDirection.Forward,
|
||||
ReferenceTypeId = ReferenceTypeIds.Aggregates,
|
||||
IncludeSubtypes = true,
|
||||
NodeClassMask = (uint)(NodeClass.Object | NodeClass.Variable | NodeClass.Method | NodeClass.ReferenceType | NodeClass.ObjectType | NodeClass.View | NodeClass.VariableType | NodeClass.DataType),
|
||||
ResultMask = (uint)BrowseResultMask.All
|
||||
};
|
||||
|
||||
BrowseDescription nodeToBrowse2 = new()
|
||||
{
|
||||
NodeId = sourceId,
|
||||
BrowseDirection = BrowseDirection.Forward,
|
||||
ReferenceTypeId = ReferenceTypeIds.Organizes,
|
||||
IncludeSubtypes = true,
|
||||
NodeClassMask = (uint)(NodeClass.Object | NodeClass.Variable | NodeClass.Method | NodeClass.View | NodeClass.ReferenceType | NodeClass.ObjectType | NodeClass.VariableType | NodeClass.DataType),
|
||||
ResultMask = (uint)BrowseResultMask.All
|
||||
};
|
||||
|
||||
BrowseDescriptionCollection nodesToBrowse = new()
|
||||
{
|
||||
nodeToBrowse1,
|
||||
nodeToBrowse2
|
||||
};
|
||||
|
||||
ReferenceDescriptionCollection references = await FormUtils.BrowseAsync(PLC.Session, nodesToBrowse, false);
|
||||
return references;
|
||||
}
|
||||
private async Task PopulateBranchAsync(OPCUATagModel model)
|
||||
{
|
||||
var sourceId = (NodeId)model.Tag.NodeId;
|
||||
model.Nodes = await PopulateBranchAsync(sourceId, isShowSubvariable: IsShowSubvariable);
|
||||
}
|
||||
|
||||
private async Task<List<OPCUATagModel>> PopulateBranchAsync(NodeId sourceId, bool isAll = false, bool isShowSubvariable = false)
|
||||
{
|
||||
if (!PLC.Connected)
|
||||
{
|
||||
return new() { new() { Name = "未完成连接", Tag = new(), Nodes = null } };
|
||||
}
|
||||
List<OPCUATagModel> nodes = new()
|
||||
{
|
||||
new OPCUATagModel() { Name = "Browsering..." }
|
||||
};
|
||||
|
||||
ReferenceDescriptionCollection references = await GetReferenceDescriptionCollectionAsync(sourceId);
|
||||
List<OPCUATagModel> list = new();
|
||||
if (references != null)
|
||||
{
|
||||
for (int ii = 0; ii < references.Count; ii++)
|
||||
{
|
||||
ReferenceDescription target = references[ii];
|
||||
OPCUATagModel child = new()
|
||||
{
|
||||
Name = Utils.Format("{0}", target),
|
||||
Tag = target
|
||||
};
|
||||
if (isShowSubvariable || target.NodeClass != NodeClass.Variable)
|
||||
{
|
||||
var data = await GetReferenceDescriptionCollectionAsync((NodeId)target.NodeId);
|
||||
if (data != null && data.Count > 0)
|
||||
{
|
||||
if (isAll)
|
||||
child.Nodes = await PopulateBranchAsync((NodeId)target.NodeId, isShowSubvariable: IsShowSubvariable);
|
||||
else
|
||||
child.Nodes = new();
|
||||
}
|
||||
else
|
||||
{
|
||||
child.Nodes = null;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
child.Nodes = null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
////if (target.NodeClass != NodeClass.Variable) //这个判断注释后会让子节点也是变量的情况下无法加载
|
||||
//{
|
||||
// var data = await GetReferenceDescriptionCollectionAsync((NodeId)target.NodeId);
|
||||
// if (data != null && data.Count > 0)
|
||||
// {
|
||||
// if (isAll)
|
||||
// child.Nodes = await PopulateBranchAsync((NodeId)target.NodeId);
|
||||
// else
|
||||
// child.Nodes = new();
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// child.Nodes = null;
|
||||
// }
|
||||
//}
|
||||
////else
|
||||
////{
|
||||
//// child.Nodes = null;
|
||||
////}
|
||||
list.Add(child);
|
||||
}
|
||||
}
|
||||
|
||||
List<OPCUATagModel> listNode = list;
|
||||
|
||||
nodes.Clear();
|
||||
nodes.AddRange(listNode.ToArray());
|
||||
return nodes;
|
||||
}
|
||||
|
||||
private async Task<TResult[]> SelectAsync<T, TResult>(IEnumerable<T> source, Func<T, Task<TResult>> selector)
|
||||
{
|
||||
return await Task.WhenAll(source.Select(selector));
|
||||
}
|
||||
internal class OPCUATagModel
|
||||
{
|
||||
internal string Name { get; set; }
|
||||
internal string NodeId => (Tag.NodeId).ToString();
|
||||
internal List<OPCUATagModel> Nodes { get; set; } = new();
|
||||
internal ReferenceDescription Tag { get; set; }
|
||||
|
||||
public List<OPCUATagModel> GetAllTags()
|
||||
{
|
||||
List<OPCUATagModel> allTags = new();
|
||||
GetAllTagsRecursive(this, allTags);
|
||||
return allTags;
|
||||
}
|
||||
|
||||
private void GetAllTagsRecursive(OPCUATagModel parentTag, List<OPCUATagModel> allTags)
|
||||
{
|
||||
allTags.Add(parentTag);
|
||||
|
||||
if (parentTag.Nodes != null)
|
||||
foreach (OPCUATagModel childTag in parentTag.Nodes)
|
||||
{
|
||||
GetAllTagsRecursive(childTag, allTags);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
@*
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
*@
|
||||
|
||||
@page "/Siemens"
|
||||
|
||||
@namespace ThingsGateway.Foundation.Demo
|
||||
@using BlazorComponent;
|
||||
@using Microsoft.AspNetCore.Components.Web;
|
||||
@using Microsoft.JSInterop;
|
||||
|
||||
@using ThingsGateway.Foundation.Adapter.Siemens;
|
||||
@using ThingsGateway.Foundation.Core;
|
||||
@using ThingsGateway.Foundation.Extension;
|
||||
@using ThingsGateway.Foundation.Serial;
|
||||
|
||||
@using Masa.Blazor
|
||||
|
||||
|
||||
<TcpClientPage @ref=_tcpClientPage></TcpClientPage>
|
||||
|
||||
<MCard Flat Class="pa-2 my-1" Style="width:100%">
|
||||
<div class="mb-4">驱动配置</div>
|
||||
<MRow Justify="JustifyTypes.Start" Align="AlignTypes.Center">
|
||||
@if (_plc != null)
|
||||
{
|
||||
|
||||
<MTextField Class="ma-1" Outlined Style="max-width:100px" Label=@(_plc.Description(x => x.FrameTime)) Dense HideDetails="@("auto")" @bind-Value=@_plc.FrameTime></MTextField>
|
||||
<MTextField Class="ma-1" Outlined Style="max-width:100px" Label=@(_plc.Description(x => x.CacheTimeout)) Dense HideDetails="@("auto")" @bind-Value=@_plc.CacheTimeout></MTextField>
|
||||
<MTextField Class="ma-1" Outlined Style="max-width:100px" Label=@(_plc.Description(x => x.TimeOut)) Dense HideDetails="@("auto")" @bind-Value=@_plc.TimeOut></MTextField>
|
||||
<MTextField Class="ma-1" Outlined Style="max-width:100px" Label=@(_plc.Description(x => x.Slot)) Dense HideDetails="@("auto")" @bind-Value=@_plc.Slot></MTextField>
|
||||
<MTextField Class="ma-1" Outlined Style="max-width:100px" Label=@(_plc.Description(x => x.Rack)) Dense HideDetails="@("auto")" @bind-Value=@_plc.Rack></MTextField>
|
||||
<MSelect Class="ma-1" Outlined Style="max-width:200px" @bind-Value="_plc.SiemensEnum" Label="@(_plc.Description(x => x.SiemensEnum))"
|
||||
Items=@(typeof(SiemensEnum).GetEnumList())
|
||||
MenuProps="@(props => { props.Auto = true; props.OffsetY = true; })"
|
||||
ItemText=@((u) =>u.Description)
|
||||
ItemValue=@(u =>(SiemensEnum)u.Value)
|
||||
HideDetails=@("auto") Height="30"
|
||||
Dense>
|
||||
</MSelect>
|
||||
|
||||
<MSelect Class="ma-1" Outlined Style="max-width:200px" @bind-Value="_plc.DataFormat" Label="@(_plc.Description(x => x.DataFormat))"
|
||||
Items=@(typeof(DataFormat).GetEnumList())
|
||||
MenuProps="@(props => { props.Auto = true; props.OffsetY = true; })"
|
||||
ItemText=@((u) =>u.Description)
|
||||
ItemValue=@(u =>(DataFormat)u.Value)
|
||||
HideDetails=@("auto") Height="30"
|
||||
Dense>
|
||||
</MSelect>
|
||||
|
||||
}
|
||||
</MRow>
|
||||
</MCard>
|
||||
|
||||
|
||||
<DriverDebugUIPage @ref=_driverDebugUIPage Sections="_sections">
|
||||
|
||||
</DriverDebugUIPage>
|
||||
|
||||
|
||||
@code {
|
||||
private readonly List<(string Code, string Language)> _sections = new();
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
#region copyright
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
#endregion
|
||||
|
||||
namespace ThingsGateway.Foundation.Demo;
|
||||
|
||||
|
||||
/// <inheritdoc/>
|
||||
public partial class SiemensDebugPage
|
||||
{
|
||||
private DriverDebugUIPage _driverDebugUIPage;
|
||||
|
||||
private ThingsGateway.Foundation.Adapter.Siemens.SiemensS7PLC _plc;
|
||||
|
||||
/// <summary>
|
||||
/// SerialSessionPage
|
||||
/// </summary>
|
||||
private TcpClientPage _tcpClientPage;
|
||||
/// <summary>
|
||||
/// <inheritdoc/>
|
||||
/// </summary>
|
||||
/// <param name="firstRender"></param>
|
||||
protected override void OnAfterRender(bool firstRender)
|
||||
{
|
||||
if (firstRender)
|
||||
{
|
||||
_sections.Add((
|
||||
"""
|
||||
private static async Task ModbusClientAsync()
|
||||
{
|
||||
//链路基础配置项
|
||||
var config = new TouchSocketConfig();
|
||||
config
|
||||
.SetRemoteIPHost(new IPHost("127.0.0.1:502"))//TCP/UDP链路才需要
|
||||
|
||||
var tcpClient1 = new TcpClient();//链路对象
|
||||
tcpClient1.Setup(config);
|
||||
|
||||
//创建协议对象,构造函数需要传入对应链路对象
|
||||
SiemensS7PLC plc = new(tcpClient1,SiemensEnum.S1500)
|
||||
{
|
||||
//协议配置
|
||||
DataFormat = DataFormat.ABCD,
|
||||
FrameTime = 0,
|
||||
CacheTimeout = 1000,
|
||||
ConnectTimeOut = 3000,
|
||||
Station = 1,
|
||||
TimeOut = 3000,
|
||||
IsCheckMessageId = true
|
||||
};
|
||||
|
||||
#region 读写测试
|
||||
var bytesResult = await plc.ReadAsync("400001", 20);
|
||||
var int32sResult = await plc.ReadInt32Async("400001", 20);
|
||||
|
||||
#endregion
|
||||
|
||||
}
|
||||
|
||||
""", "csharp"));
|
||||
|
||||
if (_tcpClientPage != null)
|
||||
_tcpClientPage.LogAction = _driverDebugUIPage.LogOut;
|
||||
_plc = new ThingsGateway.Foundation.Adapter.Siemens.SiemensS7PLC(_tcpClientPage.GetTcpClient());
|
||||
_driverDebugUIPage.Plc = _plc;
|
||||
|
||||
//初始化
|
||||
_driverDebugUIPage.Address = "M100";
|
||||
int index = 0;
|
||||
_driverDebugUIPage.DeviceVariableRunTimes.ForEach(a => a.Address = "M" + (100 + index++));
|
||||
_tcpClientPage._port = 102;
|
||||
_tcpClientPage.StateHasChangedAsync();
|
||||
|
||||
|
||||
//载入配置
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
base.OnAfterRender(firstRender);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
@*
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
*@
|
||||
|
||||
@namespace ThingsGateway.Foundation.Demo
|
||||
@inherits LayoutComponentBase
|
||||
|
||||
<CascadingValue Value="IsMobile" Name="IsMobile">
|
||||
<MApp>
|
||||
<MErrorHandler>
|
||||
@Body
|
||||
</MErrorHandler>
|
||||
</MApp>
|
||||
</CascadingValue>
|
||||
|
||||
@code {
|
||||
public bool IsMobile { get; set; }
|
||||
[Inject]
|
||||
public MasaBlazor MasaBlazor { get; set; }
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
base.OnInitialized();
|
||||
MasaBlazor.BreakpointChanged += BreakpointOnOnUpdate;
|
||||
}
|
||||
|
||||
private void BreakpointOnOnUpdate(object sender, BreakpointChangedEventArgs e)
|
||||
{
|
||||
IsMobile = MasaBlazor.Breakpoint.Mobile;
|
||||
if (e.MobileChanged)
|
||||
{
|
||||
StateHasChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
@*
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
*@
|
||||
|
||||
@namespace ThingsGateway.Foundation.Demo
|
||||
@using System.Text;
|
||||
@inherits LayoutComponentBase
|
||||
@layout BaseLayout
|
||||
|
||||
<PPageTabsProvider>
|
||||
|
||||
<CascadingValue Value="@this" IsFixed>
|
||||
<CascadingValue Value="@Changed" Name="Changed">
|
||||
|
||||
<MNavigationDrawer Color="barcolor" @bind-Value="_drawerOpen" App Width="200">
|
||||
<Logo CONFIG_COPYRIGHT=@CONFIG_COPYRIGHT CONFIG_TITLE=@CONFIG_TITLE HeightInt=@(IsMobile?BlazorResourceConst.AppBarHeight:BlazorResourceConst.AppBarHeight+BlazorResourceConst.PageTabsHeight) />
|
||||
<AppList ClassString="overflow-y-auto" Routable
|
||||
StyleString=@($"height: calc(100vh - {BlazorResourceConst.AppBarHeight+BlazorResourceConst.PageTabsHeight}px);")
|
||||
Items="_navs" />
|
||||
</MNavigationDrawer>
|
||||
|
||||
<MAppBar Color="barcolor" Style=@($"{(!(IsMobile||_drawerOpen!=true)? "left:200px;":"")}") Elevation="1" App Flat ClippedRight Dense ElevateOnScroll
|
||||
MaxHeight="@(BlazorResourceConst.AppBarHeight)" Height="@(BlazorResourceConst.AppBarHeight)">
|
||||
<MButton Class="mr-0" Icon Small=IsMobile OnClick=@(() => _drawerOpen = !_drawerOpen)>
|
||||
<MIcon>mdi-menu</MIcon>
|
||||
</MButton>
|
||||
|
||||
</MAppBar>
|
||||
|
||||
<MMain Style=@($"{(!(IsMobile||_drawerOpen!=true)? "padding-left:200px;":"")}")>
|
||||
<div class="full-width">
|
||||
<PageTabs @ref="_pageTabs" PageTabItems="_pageTabItems" />
|
||||
</div>
|
||||
<MDivider Center></MDivider>
|
||||
<MCard Flat Class="overflow-y-auto overflow-x-hidden ma-auto pa-0 rounded-0" Style=@($"height: calc(100vh - {BlazorResourceConst.AppBarHeight+BlazorResourceConst.PageTabsHeight+BlazorResourceConst.FooterHeight}px);")>
|
||||
<PPageContainer PageTabs="@_pageTabs?.PPageTabs">
|
||||
@Body
|
||||
</PPageContainer>
|
||||
</MCard>
|
||||
<MSheet Class="d-flex justify-center align-center rounded-0" Style=@($"height: {BlazorResourceConst.FooterHeight}px;")>
|
||||
<Foter CONFIG_COPYRIGHT=@CONFIG_COPYRIGHT CONFIG_COPYRIGHT_URL=@CONFIG_COPYRIGHT_URL CONFIG_TITLE=@CONFIG_TITLE></Foter>
|
||||
</MSheet>
|
||||
</MMain>
|
||||
</CascadingValue>
|
||||
</CascadingValue>
|
||||
|
||||
</PPageTabsProvider>
|
||||
@code {
|
||||
bool Changed { get; set; }
|
||||
private bool? _drawerOpen = true;
|
||||
|
||||
private PageTabs _pageTabs;
|
||||
|
||||
/// <summary>
|
||||
/// IsMobile
|
||||
/// </summary>
|
||||
[CascadingParameter(Name = "IsMobile")]
|
||||
public bool IsMobile { get; set; }
|
||||
|
||||
}
|
||||
|
||||
@code{
|
||||
private string CONFIG_COPYRIGHT = "Diego";
|
||||
private string CONFIG_COPYRIGHT_URL = "https://gitee.com/diego2098/ThingsGateway";
|
||||
private string CONFIG_TITLE = "ThingsGateway";
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,234 @@
|
||||
#region copyright
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
#endregion
|
||||
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace ThingsGateway.Foundation.Demo;
|
||||
|
||||
public partial class MainLayout
|
||||
{
|
||||
|
||||
private List<NavItem> _navs { get; set; } = new();
|
||||
private List<PageTabItem> _pageTabItems { get; set; } = new();
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
var dataString =
|
||||
"""
|
||||
[
|
||||
{
|
||||
"Href": "/index",
|
||||
"Title": "首页"
|
||||
},
|
||||
{
|
||||
"Title": "Modbus",
|
||||
"Children": [
|
||||
{
|
||||
"Href": "/ModbusRtu",
|
||||
"Title": "ModbusRtu"
|
||||
},
|
||||
{
|
||||
"Href": "/ModbusTcp",
|
||||
"Title": "ModbusTcp"
|
||||
},
|
||||
{
|
||||
"Href": "/ModbusRtuOverTcp",
|
||||
"Title": "ModbusRtuOverTcp"
|
||||
},
|
||||
{
|
||||
"Href": "/ModbusRtuOverUdp",
|
||||
"Title": "ModbusRtuOverUdp"
|
||||
},
|
||||
{
|
||||
"Href": "/ModbusUdp",
|
||||
"Title": "ModbusUdp"
|
||||
},
|
||||
{
|
||||
"Href": "/ModbusTcpDtu",
|
||||
"Title": "ModbusTcpDtu"
|
||||
},
|
||||
{
|
||||
"Href": "/ModbusTcpServer",
|
||||
"Title": "ModbusTcpServer"
|
||||
},
|
||||
{
|
||||
"Href": "/ModbusSerialServer",
|
||||
"Title": "ModbusSerialServer"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"Title": "Siemens",
|
||||
"Children": [
|
||||
{
|
||||
"Href": "/Siemens",
|
||||
"Title": "Siemens"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"Title": "DLT645",
|
||||
"Children": [
|
||||
{
|
||||
"Href": "/DLT645_2007",
|
||||
"Title": "DLT645_2007"
|
||||
},
|
||||
{
|
||||
"Href": "/DLT645_2007OverTcp",
|
||||
"Title": "DLT645_2007OverTcp"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"Title": "OPCDA",
|
||||
"Children": [
|
||||
{
|
||||
"Href": "/OPCDAClient",
|
||||
"Title": "OPCDAClient"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"Title": "OPCUA",
|
||||
"Children": [
|
||||
{
|
||||
"Href": "/OPCUAClient",
|
||||
"Title": "OPCUAClient"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"Title": "Mqtt",
|
||||
"Children": [
|
||||
{
|
||||
"Href": "/MqttClient",
|
||||
"Title": "MqttClient"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
""";
|
||||
_navs = dataString.FromJsonString<List<NavItem>>();
|
||||
|
||||
#if Pro
|
||||
var dataStringPro =
|
||||
"""
|
||||
[
|
||||
{
|
||||
"Title": "Melsec",
|
||||
"Children": [
|
||||
{
|
||||
"Href": "/QnA3E_Binary",
|
||||
"Title": "QnA3E_Binary"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"Title": "ABCIP",
|
||||
"Children": [
|
||||
{
|
||||
"Href": "/ABCIPTCP",
|
||||
"Title": "ABCIPTCP"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"Title": "Omron",
|
||||
"Children": [
|
||||
{
|
||||
"Href": "/OmronFinsTcp",
|
||||
"Title": "OmronFinsTcp"
|
||||
},
|
||||
{
|
||||
"Href": "/OmronFinsUdp",
|
||||
"Title": "OmronFinsUdp"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"Title": "Secs",
|
||||
"Children": [
|
||||
{
|
||||
"Href": "/SecsTcp",
|
||||
"Title": "SecsTcp"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"Title": "TS550",
|
||||
"Children": [
|
||||
{
|
||||
"Href": "/TS550",
|
||||
"Title": "TS550"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"Title": "Vigor",
|
||||
"Children": [
|
||||
{
|
||||
"Href": "/VigorSerial",
|
||||
"Title": "VigorSerial"
|
||||
},
|
||||
{
|
||||
"Href": "/VigorSerialOverTcp",
|
||||
"Title": "VigorSerialOverTcp"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"Title": "HZW_QTJC_01",
|
||||
"Children": [
|
||||
{
|
||||
"Href": "/HZW_QTJC_01Serial",
|
||||
"Title": "HZW_QTJC_01Serial"
|
||||
},
|
||||
{
|
||||
"Href": "/HZW_QTJC_01SerialOverTcp",
|
||||
"Title": "HZW_QTJC_01SerialOverTcp"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"Title": "LQTCP",
|
||||
"Children": [
|
||||
{
|
||||
"Href": "/LQTCP",
|
||||
"Title": "LQTCP"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"Title": "KELID2008",
|
||||
"Children": [
|
||||
{
|
||||
"Href": "/KELID2008",
|
||||
"Title": "KELID2008"
|
||||
},
|
||||
{
|
||||
"Href": "/KELID2008OverTcp",
|
||||
"Title": "KELID2008OverTcp"
|
||||
}
|
||||
]
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
""";
|
||||
Navs.AddRange(dataStringPro.FromJsonString<List<NavItem>>());
|
||||
#endif
|
||||
_pageTabItems = _navs.PasePageTabItem();
|
||||
base.OnInitialized();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Razor">
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\admin\ThingsGateway.Components\ThingsGateway.Components.csproj" />
|
||||
<ProjectReference Include="..\..\Foundation\ThingsGateway.Foundation.Adapter.DLT645\ThingsGateway.Foundation.Adapter.DLT645.csproj" />
|
||||
<ProjectReference Include="..\..\Foundation\ThingsGateway.Foundation.Adapter.Modbus\ThingsGateway.Foundation.Adapter.Modbus.csproj" />
|
||||
<ProjectReference Include="..\..\foundation\ThingsGateway.Foundation.Adapter.OPCDA\ThingsGateway.Foundation.Adapter.OPCDA.csproj" />
|
||||
<ProjectReference Include="..\..\foundation\ThingsGateway.Foundation.Adapter.OPCUA\ThingsGateway.Foundation.Adapter.OPCUA.csproj" />
|
||||
<ProjectReference Include="..\..\Foundation\ThingsGateway.Foundation.Adapter.Siemens\ThingsGateway.Foundation.Adapter.Siemens.csproj" />
|
||||
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,29 @@
|
||||
@*
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
*@
|
||||
|
||||
@using System.Net.Http
|
||||
@using Microsoft.AspNetCore.Components.Forms
|
||||
@using Microsoft.AspNetCore.Components.Routing
|
||||
@using Microsoft.AspNetCore.Components.Web
|
||||
@using Microsoft.AspNetCore.Components.Web.Virtualization
|
||||
@using Microsoft.JSInterop
|
||||
@using BlazorComponent
|
||||
@using Masa.Blazor
|
||||
@using Masa.Blazor.Presets
|
||||
@using ThingsGateway.Foundation.Core;
|
||||
@using ThingsGateway.Components;
|
||||
@using ThingsGateway.Core;
|
||||
@using System.Net.Http.Json
|
||||
@using System.IO;
|
||||
@using System.Text.Json;
|
||||
@using ThingsGateway.Foundation.Serial;
|
||||
@using ThingsGateway.Foundation.Sockets;
|
||||
@@ -0,0 +1,16 @@
|
||||
#region copyright
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
#endregion
|
||||
|
||||
global using System;
|
||||
|
||||
global using System.Windows.Forms;
|
||||
|
||||
79
framework/demo/ThingsGateway.Foundation.Demo.Winform/MainFrom.Designer.cs
generated
Normal file
79
framework/demo/ThingsGateway.Foundation.Demo.Winform/MainFrom.Designer.cs
generated
Normal file
@@ -0,0 +1,79 @@
|
||||
#region copyright
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
#endregion
|
||||
|
||||
using System.Drawing;
|
||||
|
||||
namespace ThingsGateway.Foundation.Demo.Winform
|
||||
{
|
||||
partial class MainFrom
|
||||
{
|
||||
/// <summary>
|
||||
/// Required designer variable.
|
||||
/// </summary>
|
||||
private System.ComponentModel.IContainer components = null;
|
||||
|
||||
/// <summary>
|
||||
/// Clean up any resources being used.
|
||||
/// </summary>
|
||||
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing && (components != null))
|
||||
{
|
||||
components.Dispose();
|
||||
}
|
||||
blazorWebView1.Dispose();
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
#region Windows Form Designer generated code
|
||||
|
||||
/// <summary>
|
||||
/// Required method for Designer support - do not modify
|
||||
/// the contents of this method with the code editor.
|
||||
/// </summary>
|
||||
private void InitializeComponent()
|
||||
{
|
||||
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(MainFrom));
|
||||
blazorWebView1 = new Microsoft.AspNetCore.Components.WebView.WindowsForms.BlazorWebView();
|
||||
SuspendLayout();
|
||||
//
|
||||
// blazorWebView1
|
||||
//
|
||||
blazorWebView1.Dock = DockStyle.Fill;
|
||||
blazorWebView1.Location = new Point(0, 0);
|
||||
blazorWebView1.Margin = new Padding(4);
|
||||
blazorWebView1.Name = "blazorWebView1";
|
||||
blazorWebView1.Size = new Size(1029, 529);
|
||||
blazorWebView1.TabIndex = 0;
|
||||
blazorWebView1.Text = "blazorWebView1";
|
||||
//
|
||||
// MainFrom
|
||||
//
|
||||
AutoScaleDimensions = new SizeF(9F, 20F);
|
||||
AutoScaleMode = AutoScaleMode.Font;
|
||||
ClientSize = new Size(1029, 529);
|
||||
Controls.Add(blazorWebView1);
|
||||
Icon = (Icon)resources.GetObject("$this.Icon");
|
||||
Margin = new Padding(4);
|
||||
Name = "MainFrom";
|
||||
Text = "Form1";
|
||||
FormClosed += MainFrom_FormClosed;
|
||||
ResumeLayout(false);
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private Microsoft.AspNetCore.Components.WebView.WindowsForms.BlazorWebView blazorWebView1;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
#region copyright
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
#endregion
|
||||
|
||||
using Microsoft.AspNetCore.Components.WebView.WindowsForms;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
using System.Diagnostics;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using ThingsGateway.Components;
|
||||
|
||||
namespace ThingsGateway.Foundation.Demo.Winform
|
||||
{
|
||||
public partial class MainFrom : Form
|
||||
{
|
||||
public MainFrom()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
IServiceCollection services = null;
|
||||
|
||||
Serve.RunNative(a =>
|
||||
{
|
||||
services = a;
|
||||
services.AddWindowsFormsBlazorWebView();
|
||||
services.ThingsGatewayComponentsConfigureServices();
|
||||
});
|
||||
|
||||
blazorWebView1.HostPage = "wwwroot/index.html";
|
||||
blazorWebView1.Services = services.BuildServiceProvider();
|
||||
this.Text = "ThingsGateway.Foundation.Demo";
|
||||
blazorWebView1.RootComponents.Add<ThingsGateway.Foundation.Demo.App>("#app");
|
||||
}
|
||||
|
||||
private void MainFrom_FormClosed(object sender, FormClosedEventArgs e)
|
||||
{
|
||||
|
||||
Task.Run(() => { MessageBox.Show("释放资源中,稍候自动退出程序..."); });
|
||||
Task.Run(async () =>
|
||||
{
|
||||
await Task.Delay(3000);
|
||||
Process.GetCurrentProcess().Kill();
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,197 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<assembly alias="System.Drawing" name="System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
|
||||
<data name="$this.Icon" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>
|
||||
AAABAAEAICAAAAEAIACoEAAAFgAAACgAAAAgAAAAQAAAAAEAIAAAAAAAABAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAwo82AMKPNgvCjzYkwo82JsKPNifCjzYqwo82IcKPNgLCjzYAAAAAAAAA
|
||||
AAAAAAAAAAAAAMKPNgDCjzYBwo82HsKPNknCjzZewo82QcKPNhLCjzYAwo82AAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADCjzYAwo82S8KPNtPCjzbiwo8258KPNuDCjzatwo82EsKP
|
||||
NgAAAAAAAAAAAAAAAADCjzYAwo82BsKPNmvCjzbbwo826MKPNtvCjzbhwo82vcKPNmDCjzYywo82AAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMKPNgDCjzYPwo82O8KPNs7Cjzb/wo82iMKP
|
||||
Nh7CjzYCwo82AAAAAAAAAAAAwo82AMKPNgDCjzZnwo8298KPNsLCjzY6wo82GsKPNjTCjzbJwo82/8KP
|
||||
NpTCjzYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMKPNgDCjzYIwo82vsKP
|
||||
Nv/CjzZowo82AAAAAAAAAAAAAAAAAAAAAADCjzYAwo82JMKPNtnCjzbxwo82QcKPNgDCjzYAwo82AMKP
|
||||
NpHCjzb/wo82lsKPNgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwo82AMKP
|
||||
NgjCjza9wo82/8KPNmjCjzYAAAAAAAAAAAAAAAAAwo82AMKPNgDCjzZ2wo82/8KPNrnCjzYKwo82AAAA
|
||||
AADCjzYAwo82jcKPNv/CjzaWwo82AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AADCjzYAwo82CMKPNr3Cjzb/wo82aMKPNgAAAAAAAAAAAAAAAADCjzYAwo82CcKPNrrCjzb/wo82fcKP
|
||||
NgDCjzYAAAAAAMKPNgDCjzaLwo82/8KPNpbCjzYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAMKPNgDCjzYIwo82vMKPNv/CjzZpwo82AAAAAAAAAAAAAAAAAMKPNgDCjzYfwo8238KP
|
||||
Nv3CjzZRwo82AMKPNgDCjzYBwo82B8KPNpnCjzb/wo82psKPNgrCjzYAwo82AAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAwo82AMKPNgjCjza8wo82/8KPNmnCjzYAAAAAAAAAAAAAAAAAwo82AMKP
|
||||
NjbCjzbxwo829sKPNj7CjzYAwo82AMKPNiPCjzaywo827cKPNv/Cjzbywo82qMKPNhPCjzYAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADCjzYAwo82CMKPNrvCjzb/wo82acKPNgAAAAAAAAAAAAAA
|
||||
AADCjzYAwo82P8KPNvfCjzbzwo82OcKPNgDCjzYAwo82D8KPNlnCjzZiwo82X8KPNmDCjzZRwo82CcKP
|
||||
NgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMKPNgDCjzYHwo82usKPNv/CjzZpwo82AAAA
|
||||
AAAAAAAAAAAAAMKPNgDCjzY+wo829sKPNvTCjzY8wo82AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwo82AMKPNgfCjza6wo82/8KP
|
||||
NmnCjzYAAAAAAAAAAAAAAAAAwo82AMKPNirCjzbpwo82+MKPNkDCjzYAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADCjzYAwo82B8KP
|
||||
NrnCjzb/wo82acKPNgAAAAAAAAAAAAAAAADCjzYAwo82FcKPNtLCjzb/wo82VsKPNgAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwo82AMKPNgLCjzYTwo82BMKP
|
||||
NgDCjzYHwo82t8KPNv/CjzZpwo82AMKPNgDCjzYMwo82E8KPNgDCjzYCwo82n8KPNv/CjzaEwo82AMKP
|
||||
NgAAAAAAwo82AMKPNgDCjzZPwo82XcKPNgLCjzYAAAAAAAAAAAAAAAAAAAAAAAAAAADCjzYAwo82F8KP
|
||||
NrTCjzY6wo82AMKPNgbCjza0wo82/8KPNmnCjzYAwo82AMKPNnPCjzaawo82A8KPNgDCjzZOwo82+MKP
|
||||
NsbCjzYRwo82AAAAAADCjzYAwo82DcKPNsLCjzagwo82AMKPNgAAAAAAAAAAAAAAAAAAAAAAAAAAAMKP
|
||||
NgDCjzYPwo82ysKPNpPCjzYBwo82BcKPNrHCjzb/wo82acKPNgDCjzYUwo82zMKPNpPCjzYAwo82AMKP
|
||||
NgrCjzanwo82/MKPNmnCjzYAwo82AMKPNgDCjzZRwo8298KPNnbCjzYAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAwo82AMKPNgDCjzaawo827cKPNmHCjzYswo82vMKPNv/CjzaEwo82K8KPNoDCjzb5wo82ZMKP
|
||||
NgAAAAAAwo82AMKPNi7CjzbWwo825sKPNlnCjzYdwo82SMKPNtTCjzb9wo82UsKPNgAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAwo82AMKPNmHCjzb3wo828cKPNt/Cjzbuwo8298KPNurCjzbjwo829MKP
|
||||
NurCjzY6wo82AAAAAADCjzYAwo82AMKPNjfCjza8wo826cKPNtvCjzbewo82ycKPNs7CjzYvwo82AAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADCjzYAwo82EsKPNj3CjzZAwo82QcKPNkDCjzY/wo82QMKP
|
||||
NkDCjzY/wo82OMKPNgrCjzYAAAAAAAAAAADCjzYAwo82AMKPNg/CjzY5wo82SsKPNjDCjzYOwo82G8KP
|
||||
NgXCjzYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAA//////////////////////////////////////wH4H/8B8Af/AfAH/4f
|
||||
hx/+H4cf/h8PH/4fDAf+HwwH/h8MB/4fD//+Hw///h8P/+IZD4/iGIcP4BGHH+ABwB/wAeAf8AHwH///
|
||||
//////////////////////////////////8=
|
||||
</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -0,0 +1,29 @@
|
||||
#region copyright
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
#endregion
|
||||
|
||||
namespace ThingsGateway.Foundation.Demo.Winform
|
||||
{
|
||||
internal static class Program
|
||||
{
|
||||
/// <summary>
|
||||
/// The main entry point for the application.
|
||||
/// </summary>
|
||||
[STAThread]
|
||||
static void Main()
|
||||
{
|
||||
// To customize application configuration such as set high DPI settings or default font,
|
||||
// see https://aka.ms/applicationconfiguration.
|
||||
ApplicationConfiguration.Initialize();
|
||||
Application.Run(new MainFrom());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Razor">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<ApplicationIcon>favicon.ico</ApplicationIcon>
|
||||
<TargetFrameworks>net6.0-windows;net8.0-windows</TargetFrameworks>
|
||||
<UseWindowsForms>true</UseWindowsForms>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Remove="favicon.ico" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="favicon.ico">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
|
||||
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition="'$(TargetFramework)'=='net6.0-windows'">
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebView.WindowsForms" Version="6.0.553" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Condition="'$(TargetFramework)'=='net8.0-windows'">
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebView.WindowsForms" Version="8.0.3" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\ThingsGateway.Foundation.Demo.Rcl\ThingsGateway.Foundation.Demo.Rcl.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
<ItemGroup>
|
||||
<Content Update="wwwroot\favicon.ico">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="wwwroot\favicon.png">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="wwwroot\index.html">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
|
||||
</Project>
|
||||
BIN
framework/demo/ThingsGateway.Foundation.Demo.Winform/favicon.ico
Normal file
BIN
framework/demo/ThingsGateway.Foundation.Demo.Winform/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.2 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 4.2 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 10 KiB |
@@ -0,0 +1,41 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover" />
|
||||
<title>ThingsGateway.Foundation.Demo</title>
|
||||
<base href="/" />
|
||||
|
||||
<link rel="icon" href="favicon.ico" type="image/x-icon">
|
||||
|
||||
|
||||
<link href="_content/Masa.Blazor/css/masa-blazor.min.css" rel="stylesheet" />
|
||||
<link href="_content/ThingsGateway.Components/css/materialdesign/v7.1.96/css/materialdesignicons.min.css" rel="stylesheet">
|
||||
<link href="_content/ThingsGateway.Components/css/material/icons.css" rel="stylesheet">
|
||||
<link href="_content/ThingsGateway.Components/css/fontawesome/v6.4.0/css/all.min.css" rel="stylesheet">
|
||||
<link href="_content/ThingsGateway.Components/style/custom.css" rel="stylesheet">
|
||||
<link href="_content/ThingsGateway.Components/prism/prism-material-dark-for-masa.css" rel="stylesheet">
|
||||
<link href="_content/ThingsGateway.Components/prism/prism-line-highlight.min.css" rel="stylesheet">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
|
||||
<div id="blazor-error-ui">
|
||||
<span>
|
||||
<environment include="Staging,Production">
|
||||
An error has occurred. This application may no longer respond until reloaded.
|
||||
</environment>
|
||||
<environment include="Development">
|
||||
An unhandled exception has occurred. See browser dev tools for details.
|
||||
</environment>
|
||||
</span>
|
||||
<a href="" class="reload">Reload</a>
|
||||
<a class="dismiss">🗙</a>
|
||||
</div>
|
||||
|
||||
<script src="_framework/blazor.webview.js" autostart="true"></script>
|
||||
<script src="_content/ThingsGateway.Components/prism/prism.min.js"></script>
|
||||
<script src="_content/BlazorComponent/js/blazor-component.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
53
framework/foundation/Directory.Build.props
Normal file
53
framework/foundation/Directory.Build.props
Normal file
@@ -0,0 +1,53 @@
|
||||
<Project>
|
||||
<PropertyGroup>
|
||||
<Version>4.0.0.0</Version>
|
||||
<GenerateDocumentationFile>True</GenerateDocumentationFile>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<TargetFrameworks>net45;netstandard2.0;net6.0;net8.0;</TargetFrameworks>
|
||||
<Description>
|
||||
ThingsGateway.Foundation是工业设备通讯类库,归属于ThingsGateway边缘网关项目,说明文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||
</Description>
|
||||
<Authors>Diego</Authors>
|
||||
<Product>ThingsGateway</Product>
|
||||
<Copyright>© 2023-present Diego</Copyright>
|
||||
<RepositoryUrl>https://gitee.com/diego2098/ThingsGateway</RepositoryUrl>
|
||||
<PublishRepositoryUrl>true</PublishRepositoryUrl>
|
||||
<EmbedUntrackedSource>true</EmbedUntrackedSource>
|
||||
<EmbedAllSources>true</EmbedAllSources>
|
||||
<RepositoryType>Gitee</RepositoryType>
|
||||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
||||
<PackageReadmeFile>README.md</PackageReadmeFile>
|
||||
<PackageIcon>icon.png</PackageIcon>
|
||||
<IncludeSymbols>true</IncludeSymbols>
|
||||
<PackageLicenseExpression>Apache-2.0</PackageLicenseExpression>
|
||||
<PackageProjectUrl>https://diego2098.gitee.io/thingsgateway-docs/</PackageProjectUrl>
|
||||
<PackageTags>ThingsGateway;Diego;dotNET China;Blazor;设备采集;边缘网关</PackageTags>
|
||||
<SignAssembly>True</SignAssembly>
|
||||
<DelaySign>False</DelaySign>
|
||||
<SatelliteResourceLanguages>zh-Hans</SatelliteResourceLanguages>
|
||||
<PackageOutputPath>..\..\nupkgs</PackageOutputPath>
|
||||
<AssemblyOriginatorKeyFile>..\..\..\snks/ThingsGateway.snk</AssemblyOriginatorKeyFile>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
</PropertyGroup>
|
||||
|
||||
|
||||
<ItemGroup>
|
||||
<None Include="..\..\..\README.md" Pack="true" PackagePath="\" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Include="..\..\..\icon.png">
|
||||
<Pack>True</Pack>
|
||||
<PackagePath></PackagePath>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)' == 'Release'">
|
||||
<DebugSymbols>True</DebugSymbols>
|
||||
<DebugType>Embedded</DebugType>
|
||||
<EmbedAllSources>True</EmbedAllSources>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
Binary file not shown.
@@ -0,0 +1,56 @@
|
||||
#region copyright
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
#endregion
|
||||
|
||||
namespace ThingsGateway.Foundation.Adapter.DLT645;
|
||||
|
||||
/// <summary>
|
||||
/// 控制码
|
||||
/// </summary>
|
||||
internal enum ControlCode : byte
|
||||
{
|
||||
/// <summary>
|
||||
/// 读数据
|
||||
/// </summary>
|
||||
Read = 0x11,
|
||||
/// <summary>
|
||||
/// 读后续数据
|
||||
/// </summary>
|
||||
ReadSub = 0x12,
|
||||
/// <summary>
|
||||
/// 读站号
|
||||
/// </summary>
|
||||
ReadStation = 0x13,
|
||||
/// <summary>
|
||||
/// 写数据
|
||||
/// </summary>
|
||||
Write = 0x14,
|
||||
/// <summary>
|
||||
/// 写站号
|
||||
/// </summary>
|
||||
WriteStation = 0x15,
|
||||
/// <summary>
|
||||
/// 广播校时
|
||||
/// </summary>
|
||||
BroadcastTime = 0x08,
|
||||
/// <summary>
|
||||
/// 冻结
|
||||
/// </summary>
|
||||
Freeze = 0x16,
|
||||
/// <summary>
|
||||
/// 更新波特率
|
||||
/// </summary>
|
||||
WriteBaudRate = 0x17,
|
||||
/// <summary>
|
||||
/// 更新密码
|
||||
/// </summary>
|
||||
WritePassword = 0x18,
|
||||
}
|
||||
Binary file not shown.
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,179 @@
|
||||
#region copyright
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
#endregion
|
||||
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace ThingsGateway.Foundation.Adapter.DLT645;
|
||||
/// <summary>
|
||||
/// DLT645_2007
|
||||
/// </summary>
|
||||
public class DLT645_2007 : ReadWriteDevicesSerialSessionBase, IDLT645_2007
|
||||
{
|
||||
/// <summary>
|
||||
/// DLT645_2007
|
||||
/// </summary>
|
||||
/// <param name="serialSession"></param>
|
||||
public DLT645_2007(SerialSession serialSession) : base(serialSession)
|
||||
{
|
||||
ThingsGatewayBitConverter = new DLT645_2007BitConverter(EndianType.Big);
|
||||
RegisterByteLength = 2;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
[Description("前导符报文头")]
|
||||
public bool EnableFEHead { get; set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
[Description("操作员代码")]
|
||||
public string OperCode { get; set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
[Description("写入密码")]
|
||||
public string Password { get; set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
[Description("通讯地址")]
|
||||
public string Station { get; set; }
|
||||
/// <inheritdoc/>
|
||||
public override string GetAddressDescription()
|
||||
{
|
||||
return base.GetAddressDescription() + Environment.NewLine + DLT645_2007Util.GetAddressDescription();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override List<T> LoadSourceRead<T, T2>(List<T2> deviceVariables, int maxPack, int defaultIntervalTime)
|
||||
{
|
||||
return PackHelper.LoadSourceRead<T, T2>(this, deviceVariables, maxPack, defaultIntervalTime);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void SetDataAdapter(ISocketClient socketClient = default)
|
||||
{
|
||||
var dataHandleAdapter = new DLT645_2007DataHandleAdapter
|
||||
{
|
||||
EnableFEHead = EnableFEHead
|
||||
};
|
||||
SerialSession.SetDataHandlingAdapter(dataHandleAdapter);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override OperResult<byte[]> Read(string address, int length, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return DLT645_2007Util.Read(this, address, length, cancellationToken);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override Task<OperResult<byte[]>> ReadAsync(string address, int length, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return DLT645_2007Util.ReadAsync(this, address, length, cancellationToken);
|
||||
}
|
||||
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override OperResult Write(string address, string value, CancellationToken cancellationToken = default)
|
||||
{
|
||||
|
||||
return DLT645_2007Util.Write(this, address, value, cancellationToken);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override Task<OperResult> WriteAsync(string address, string value, CancellationToken cancellationToken = default)
|
||||
{
|
||||
|
||||
return DLT645_2007Util.WriteAsync(this, address, value, cancellationToken);
|
||||
|
||||
}
|
||||
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override OperResult Write(string address, byte[] value, CancellationToken cancellationToken = default) => Write(address, value.ToString(), cancellationToken);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override OperResult Write(string address, bool[] value, CancellationToken cancellationToken = default) => Write(address, value.ToString(), cancellationToken);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override Task<OperResult> WriteAsync(string address, byte[] value, CancellationToken cancellationToken = default) => throw new NotImplementedException();
|
||||
/// <inheritdoc/>
|
||||
public override Task<OperResult> WriteAsync(string address, uint value, CancellationToken cancellationToken = default) => WriteAsync(address, value.ToString(), cancellationToken);
|
||||
/// <inheritdoc/>
|
||||
public override Task<OperResult> WriteAsync(string address, double value, CancellationToken cancellationToken = default) => WriteAsync(address, value.ToString(), cancellationToken);
|
||||
/// <inheritdoc/>
|
||||
public override Task<OperResult> WriteAsync(string address, float value, CancellationToken cancellationToken = default) => WriteAsync(address, value.ToString(), cancellationToken);
|
||||
/// <inheritdoc/>
|
||||
public override Task<OperResult> WriteAsync(string address, long value, CancellationToken cancellationToken = default) => WriteAsync(address, value.ToString(), cancellationToken);
|
||||
/// <inheritdoc/>
|
||||
public override Task<OperResult> WriteAsync(string address, ulong value, CancellationToken cancellationToken = default) => WriteAsync(address, value.ToString(), cancellationToken);
|
||||
/// <inheritdoc/>
|
||||
public override Task<OperResult> WriteAsync(string address, ushort value, CancellationToken cancellationToken = default) => WriteAsync(address, value.ToString(), cancellationToken);
|
||||
/// <inheritdoc/>
|
||||
public override Task<OperResult> WriteAsync(string address, short value, CancellationToken cancellationToken = default) => WriteAsync(address, value.ToString(), cancellationToken);
|
||||
/// <inheritdoc/>
|
||||
public override Task<OperResult> WriteAsync(string address, int value, CancellationToken cancellationToken = default) => WriteAsync(address, value.ToString(), cancellationToken);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override Task<OperResult> WriteAsync(string address, bool[] value, CancellationToken cancellationToken = default) => throw new NotImplementedException();
|
||||
|
||||
|
||||
#region 其他方法
|
||||
|
||||
/// <inheritdoc cref="DLT645_2007Util.BroadcastTime(IDLT645_2007,DateTime, CancellationToken)"/>
|
||||
public OperResult BroadcastTime(DateTime dateTime, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return DLT645_2007Util.BroadcastTime(this, dateTime, cancellationToken);
|
||||
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="DLT645_2007Util.FreezeAsync(IDLT645_2007,DateTime, CancellationToken)"/>
|
||||
public async Task<OperResult> FreezeAsync(DateTime dateTime, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return await DLT645_2007Util.FreezeAsync(this, dateTime, cancellationToken);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <inheritdoc cref="DLT645_2007Util.ReadDeviceStationAsync(IDLT645_2007, CancellationToken)"/>
|
||||
public async Task<OperResult<string>> ReadDeviceStationAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
return await DLT645_2007Util.ReadDeviceStationAsync(this, cancellationToken);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <inheritdoc cref="DLT645_2007Util.WriteBaudRateAsync(IDLT645_2007, int, CancellationToken)"/>
|
||||
public async Task<OperResult> WriteBaudRateAsync(int baudRate, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return await DLT645_2007Util.WriteBaudRateAsync(this, baudRate, cancellationToken);
|
||||
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="DLT645_2007Util.WriteDeviceStationAsync(IDLT645_2007, string, CancellationToken)"/>
|
||||
public async Task<OperResult> WriteDeviceStationAsync(string station, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return await DLT645_2007Util.WriteDeviceStationAsync(this, station, cancellationToken);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="DLT645_2007Util.WritePasswordAsync(IDLT645_2007, byte,string,string, CancellationToken)"/>
|
||||
public async Task<OperResult> WritePasswordAsync(byte level, string oldPassword, string newPassword, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return await DLT645_2007Util.WritePasswordAsync(this, level, oldPassword, newPassword, cancellationToken);
|
||||
}
|
||||
#endregion
|
||||
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
#region copyright
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
#endregion
|
||||
|
||||
using System.Text;
|
||||
|
||||
using ThingsGateway.Foundation.Extension.String;
|
||||
|
||||
namespace ThingsGateway.Foundation.Adapter.DLT645;
|
||||
|
||||
/// <summary>
|
||||
/// DLT645_2007Address
|
||||
/// </summary>
|
||||
public class DLT645_2007Address : DeviceAddressBase
|
||||
{
|
||||
/// <summary>
|
||||
/// <inheritdoc/>
|
||||
/// </summary>
|
||||
public DLT645_2007Address()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 数据标识
|
||||
/// </summary>
|
||||
public byte[] DataId { get; set; } = new byte[0];
|
||||
/// <summary>
|
||||
/// 反转解析
|
||||
/// </summary>
|
||||
public bool Reverse { get; set; } = true;
|
||||
/// <summary>
|
||||
/// 站号信息
|
||||
/// </summary>
|
||||
public byte[] Station { get; set; } = new byte[0];
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 解析地址
|
||||
/// </summary>
|
||||
/// <param name="address"></param>
|
||||
/// <returns></returns>
|
||||
public static DLT645_2007Address ParseFrom(string address)
|
||||
{
|
||||
DLT645_2007Address dlt645_2007Address = new();
|
||||
byte[] array;
|
||||
array = new byte[0];
|
||||
if (address.IndexOf(';') < 0)
|
||||
{
|
||||
array = address.ByHexStringToBytes().Reverse().ToArray();
|
||||
}
|
||||
else
|
||||
{
|
||||
string[] strArray = address.SplitStringBySemicolon();
|
||||
|
||||
for (int index = 0; index < strArray.Length; ++index)
|
||||
{
|
||||
if (strArray[index].ToUpper().StartsWith("S="))
|
||||
{
|
||||
var station = strArray[index].Substring(2);
|
||||
if (station.IsNullOrEmpty()) station = string.Empty;
|
||||
if (station.Length < 12)
|
||||
station = station.PadLeft(12, '0');
|
||||
dlt645_2007Address.Station = station.ByHexStringToBytes().Reverse().ToArray();
|
||||
}
|
||||
else if (strArray[index].Contains("r="))
|
||||
{
|
||||
dlt645_2007Address.Reverse = strArray[index].Substring(2).ToBool(false);
|
||||
}
|
||||
else if (!strArray[index].Contains("="))
|
||||
{
|
||||
array = strArray[index].ByHexStringToBytes().Reverse().ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
dlt645_2007Address.DataId = array;
|
||||
return dlt645_2007Address;
|
||||
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string ToString()
|
||||
{
|
||||
StringBuilder stringGeter = new();
|
||||
if (Station.Length > 0)
|
||||
{
|
||||
stringGeter.Append($"s={Station.Reverse().ToArray().ToHexString()};");
|
||||
}
|
||||
if (DataId.Length > 0)
|
||||
{
|
||||
stringGeter.Append($"{DataId.Reverse().ToArray().ToHexString()};");
|
||||
}
|
||||
if (!Reverse)
|
||||
{
|
||||
stringGeter.Append($"s={Reverse};");
|
||||
}
|
||||
return stringGeter.ToString();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,151 @@
|
||||
#region copyright
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
#endregion
|
||||
|
||||
using System.Text;
|
||||
|
||||
using ThingsGateway.Foundation.Extension.Generic;
|
||||
|
||||
namespace ThingsGateway.Foundation.Adapter.DLT645;
|
||||
/// <summary>
|
||||
/// DLT645_2007
|
||||
/// </summary>
|
||||
public class DLT645_2007BitConverter : ThingsGatewayBitConverter
|
||||
{
|
||||
/// <summary>
|
||||
/// DLT645_2007
|
||||
/// </summary>
|
||||
public DLT645_2007BitConverter(EndianType endianType) : base(endianType)
|
||||
{
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public override short ToInt16(byte[] buffer, int offset)
|
||||
{
|
||||
return Convert.ToInt16(this.ToString(buffer, offset, buffer.Length));
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public override ushort ToUInt16(byte[] buffer, int offset)
|
||||
{
|
||||
return Convert.ToUInt16(this.ToString(buffer, offset, buffer.Length));
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public override float ToSingle(byte[] buffer, int offset)
|
||||
{
|
||||
return Convert.ToSingle(this.ToString(buffer, offset, buffer.Length));
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public override long ToInt64(byte[] buffer, int offset)
|
||||
{
|
||||
return Convert.ToInt64(this.ToString(buffer, offset, buffer.Length));
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public override int ToInt32(byte[] buffer, int offset)
|
||||
{
|
||||
return Convert.ToInt32(this.ToString(buffer, offset, buffer.Length));
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public override uint ToUInt32(byte[] buffer, int offset)
|
||||
{
|
||||
return Convert.ToUInt32(this.ToString(buffer, offset, buffer.Length));
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public override ulong ToUInt64(byte[] buffer, int offset)
|
||||
{
|
||||
return Convert.ToUInt64(this.ToString(buffer, offset, buffer.Length));
|
||||
}
|
||||
/// <summary>
|
||||
/// DLT645协议转换double
|
||||
/// </summary>
|
||||
/// <param name="buffer">带数据项标识</param>
|
||||
/// <param name="offset"></param>
|
||||
/// <returns></returns>
|
||||
public override double ToDouble(byte[] buffer, int offset)
|
||||
{
|
||||
return Convert.ToDouble(this.ToString(buffer, offset, buffer.Length));
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override IThingsGatewayBitConverter CopyNew()
|
||||
{
|
||||
return new DLT645_2007BitConverter(EndianType)
|
||||
{
|
||||
DataFormat = DataFormat,
|
||||
BcdFormat = BcdFormat,
|
||||
Encoding = Encoding,
|
||||
IsStringReverseByteWord = IsStringReverseByteWord,
|
||||
Length = Length,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string ToString(byte[] buffer)
|
||||
{
|
||||
return this.ToString(buffer, 0, buffer.Length);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string ToString(byte[] buffer, int offset, int length)
|
||||
{
|
||||
buffer = buffer.RemoveBegin(offset);
|
||||
buffer = buffer.BytesAdd(-0x33);
|
||||
var dataInfos = DLT645Helper.GetDataInfos(buffer);
|
||||
StringBuilder stringBuilder = new();
|
||||
foreach (var dataInfo in dataInfos)
|
||||
{
|
||||
//实际数据
|
||||
var content = buffer.SelectMiddle(4, dataInfo.ByteLength).Reverse().ToArray();
|
||||
if (dataInfo.IsSigned)//可能为负数
|
||||
{
|
||||
if (content[0] > 0x80)//最高位是表示正负
|
||||
{
|
||||
content[0] = (byte)(content[0] - 0x80);
|
||||
if (dataInfo.Digtal == 0)//无小数点
|
||||
{
|
||||
stringBuilder.Append($"-{content.ToHexString()}");
|
||||
}
|
||||
else
|
||||
{
|
||||
stringBuilder.Append((-(Convert.ToDouble(content.ToHexString()) / Math.Pow(10.0, dataInfo.Digtal))).ToString());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ToString(stringBuilder, dataInfo, content);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ToString(stringBuilder, dataInfo, content);
|
||||
}
|
||||
}
|
||||
|
||||
return stringBuilder.ToString();
|
||||
|
||||
static void ToString(StringBuilder stringBuilder, DataInfo dataInfo, byte[] content)
|
||||
{
|
||||
if (dataInfo.Digtal < 0)
|
||||
{
|
||||
stringBuilder.Append($"{Encoding.ASCII.GetString(content)}");
|
||||
}
|
||||
else if (dataInfo.Digtal == 0)//无小数点
|
||||
{
|
||||
stringBuilder.Append($"{content.ToHexString()}");
|
||||
}
|
||||
else
|
||||
{
|
||||
stringBuilder.Append(((Convert.ToDouble(content.ToHexString()) / Math.Pow(10.0, dataInfo.Digtal))).ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,191 @@
|
||||
#region copyright
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
#endregion
|
||||
|
||||
using System.ComponentModel;
|
||||
|
||||
using ThingsGateway.Foundation.Extension.Generic;
|
||||
|
||||
namespace ThingsGateway.Foundation.Adapter.DLT645;
|
||||
|
||||
/// <summary>
|
||||
/// DLT645_2007DataHandleAdapter
|
||||
/// </summary>
|
||||
internal class DLT645_2007DataHandleAdapter : ReadWriteDevicesSingleStreamDataHandleAdapter<DLT645_2007Message>
|
||||
{
|
||||
/// <summary>
|
||||
/// 增加FE FE FE FE的报文头部
|
||||
/// </summary>
|
||||
[Description("前导符报文头")]
|
||||
public bool EnableFEHead { get; set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override byte[] PackCommand(byte[] command)
|
||||
{
|
||||
//打包时加上4个FE字节
|
||||
if (EnableFEHead)
|
||||
{
|
||||
return DataTransUtil.SpliceArray(new byte[4] { 0xFE, 0xFE, 0xFE, 0xFE }, command);
|
||||
}
|
||||
return command;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override DLT645_2007Message GetInstance()
|
||||
{
|
||||
return new DLT645_2007Message();
|
||||
}
|
||||
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override FilterResult UnpackResponse(DLT645_2007Message request, byte[] send, byte[] body, byte[] response)
|
||||
{
|
||||
//因为设备可能带有FE前导符开头,这里找到0x68的位置
|
||||
int headCodeIndex = -1;
|
||||
if (response != null)
|
||||
{
|
||||
for (int index = 0; index < response.Length; index++)
|
||||
{
|
||||
if (response[index] == 0x68)
|
||||
{
|
||||
headCodeIndex = index;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
int sendHeadCodeIndex = 0;
|
||||
if (send != null)
|
||||
{
|
||||
for (int index = 0; index < send.Length; index++)
|
||||
{
|
||||
if (send[index] == 0x68)
|
||||
{
|
||||
sendHeadCodeIndex = index;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//帧起始符 地址域 帧起始符 控制码 数据域长度共10个字节
|
||||
if (headCodeIndex < 0 || headCodeIndex + 10 > response.Length)
|
||||
return FilterResult.Cache;
|
||||
|
||||
|
||||
var len = 10 + response[headCodeIndex + 9] + 2;
|
||||
|
||||
if (response.Length - headCodeIndex < len)
|
||||
{
|
||||
return FilterResult.Cache;
|
||||
}
|
||||
if (response.Length - headCodeIndex >= len && response[len + headCodeIndex - 1] == 0x16)
|
||||
{
|
||||
|
||||
//检查校验码
|
||||
int sumCheck = 0;
|
||||
for (int i = headCodeIndex; i < len + headCodeIndex - 2; i++)
|
||||
sumCheck += response[i];
|
||||
if ((byte)sumCheck != response[len + headCodeIndex - 2])
|
||||
{
|
||||
//校验错误
|
||||
request.Message = "和校验错误";
|
||||
request.ErrorCode = 999;
|
||||
return FilterResult.Success;
|
||||
}
|
||||
|
||||
if (
|
||||
(response[headCodeIndex + 1] != send[sendHeadCodeIndex + 1]) ||
|
||||
(response[headCodeIndex + 2] != send[sendHeadCodeIndex + 2]) ||
|
||||
(response[headCodeIndex + 3] != send[sendHeadCodeIndex + 3]) ||
|
||||
(response[headCodeIndex + 4] != send[sendHeadCodeIndex + 4]) ||
|
||||
(response[headCodeIndex + 5] != send[sendHeadCodeIndex + 5]) ||
|
||||
(response[headCodeIndex + 6] != send[sendHeadCodeIndex + 6])
|
||||
)//设备地址不符合时,返回错误
|
||||
{
|
||||
|
||||
if (
|
||||
(send[sendHeadCodeIndex + 1] == 0xAA) &&
|
||||
(send[sendHeadCodeIndex + 2] == 0xAA) &&
|
||||
(send[sendHeadCodeIndex + 3] == 0xAA) &&
|
||||
(send[sendHeadCodeIndex + 4] == 0xAA) &&
|
||||
(send[sendHeadCodeIndex + 5] == 0xAA) &&
|
||||
(send[sendHeadCodeIndex + 6] == 0xAA)
|
||||
)//读写通讯地址例外
|
||||
{
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
request.Message = "返回地址不符合规则";
|
||||
request.ErrorCode = 999;
|
||||
return FilterResult.Success;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
if ((response[headCodeIndex + 8] != send[sendHeadCodeIndex + 8] + 0x80))//控制码不符合时,返回错误
|
||||
{
|
||||
request.Message = $"返回控制码:0x{response[headCodeIndex + 8]:X2},请求控制码:0x{send[sendHeadCodeIndex + 8]:X2},不符合规则";
|
||||
request.ErrorCode = 999;
|
||||
return FilterResult.Success;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
if ((response[headCodeIndex + 8] & 0x40) == 0x40)//控制码bit6为1时,返回错误
|
||||
{
|
||||
byte byte1 = (byte)(response[headCodeIndex + 10] - 0x33);
|
||||
var error = DLT645Helper.Get2007ErrorMessage(byte1);
|
||||
request.Message = $"异常控制码:0x{response[headCodeIndex + 8]:X2},错误信息:{error}";
|
||||
request.ErrorCode = 999;
|
||||
return FilterResult.Success;
|
||||
}
|
||||
|
||||
if (send[sendHeadCodeIndex + 8] == (byte)ControlCode.Read ||
|
||||
send[sendHeadCodeIndex + 8] == (byte)ControlCode.Write
|
||||
)
|
||||
{
|
||||
//数据标识不符合时,返回错误
|
||||
if (
|
||||
(response[headCodeIndex + 10] == send[sendHeadCodeIndex + 10]) &&
|
||||
(response[headCodeIndex + 11] == send[sendHeadCodeIndex + 11]) &&
|
||||
(response[headCodeIndex + 12] == send[sendHeadCodeIndex + 12]) &&
|
||||
(response[headCodeIndex + 13] == send[sendHeadCodeIndex + 13])
|
||||
)
|
||||
{
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
request.Message = "返回数据标识不符合规则";
|
||||
request.ErrorCode = 999;
|
||||
return FilterResult.Success;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
request.Content = response.RemoveBegin(headCodeIndex + 10).RemoveLast(response.Length + 2 - len - headCodeIndex);
|
||||
request.ErrorCode = 0;
|
||||
return FilterResult.Success;
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
request.ErrorCode = 999;
|
||||
return FilterResult.Success;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
#region copyright
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
#endregion
|
||||
|
||||
namespace ThingsGateway.Foundation.Adapter.DLT645;
|
||||
/// <summary>
|
||||
/// <inheritdoc/>
|
||||
/// </summary>
|
||||
internal class DLT645_2007Message : MessageBase, IMessage
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public override int HeadBytesLength => -1;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool CheckHeadBytes(byte[] heads)
|
||||
{
|
||||
BodyLength = -1;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,180 @@
|
||||
#region copyright
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
#endregion
|
||||
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace ThingsGateway.Foundation.Adapter.DLT645;
|
||||
/// <summary>
|
||||
/// DLT645_2007
|
||||
/// </summary>
|
||||
public class DLT645_2007OverTcp : ReadWriteDevicesTcpClientBase, IDLT645_2007
|
||||
{
|
||||
/// <summary>
|
||||
/// DLT645_2007
|
||||
/// </summary>
|
||||
/// <param name="tcpClient"></param>
|
||||
public DLT645_2007OverTcp(TcpClient tcpClient) : base(tcpClient)
|
||||
{
|
||||
ThingsGatewayBitConverter = new DLT645_2007BitConverter(EndianType.Big);
|
||||
RegisterByteLength = 2;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
[Description("前导符报文头")]
|
||||
public bool EnableFEHead { get; set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
[Description("操作员代码")]
|
||||
public string OperCode { get; set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
[Description("写入密码")]
|
||||
public string Password { get; set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
[Description("通讯地址")]
|
||||
public string Station { get; set; }
|
||||
/// <inheritdoc/>
|
||||
public override string GetAddressDescription()
|
||||
{
|
||||
return base.GetAddressDescription() + Environment.NewLine + DLT645_2007Util.GetAddressDescription();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override List<T> LoadSourceRead<T, T2>(List<T2> deviceVariables, int maxPack, int defaultIntervalTime)
|
||||
{
|
||||
return PackHelper.LoadSourceRead<T, T2>(this, deviceVariables, maxPack, defaultIntervalTime);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void SetDataAdapter(ISocketClient socketClient = default)
|
||||
{
|
||||
var dataHandleAdapter = new DLT645_2007DataHandleAdapter
|
||||
{
|
||||
EnableFEHead = EnableFEHead
|
||||
};
|
||||
TcpClient.SetDataHandlingAdapter(dataHandleAdapter);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override OperResult<byte[]> Read(string address, int length, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return DLT645_2007Util.Read(this, address, length, cancellationToken);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override Task<OperResult<byte[]>> ReadAsync(string address, int length, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return DLT645_2007Util.ReadAsync(this, address, length, cancellationToken);
|
||||
}
|
||||
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override OperResult Write(string address, string value, CancellationToken cancellationToken = default)
|
||||
{
|
||||
|
||||
return DLT645_2007Util.Write(this, address, value, cancellationToken);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override Task<OperResult> WriteAsync(string address, string value, CancellationToken cancellationToken = default)
|
||||
{
|
||||
|
||||
return DLT645_2007Util.WriteAsync(this, address, value, cancellationToken);
|
||||
|
||||
}
|
||||
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override OperResult Write(string address, byte[] value, CancellationToken cancellationToken = default) => Write(address, value.ToString(), cancellationToken);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override OperResult Write(string address, bool[] value, CancellationToken cancellationToken = default) => Write(address, value.ToString(), cancellationToken);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override Task<OperResult> WriteAsync(string address, byte[] value, CancellationToken cancellationToken = default) => throw new NotImplementedException();
|
||||
/// <inheritdoc/>
|
||||
public override Task<OperResult> WriteAsync(string address, uint value, CancellationToken cancellationToken = default) => WriteAsync(address, value.ToString(), cancellationToken);
|
||||
/// <inheritdoc/>
|
||||
public override Task<OperResult> WriteAsync(string address, double value, CancellationToken cancellationToken = default) => WriteAsync(address, value.ToString(), cancellationToken);
|
||||
/// <inheritdoc/>
|
||||
public override Task<OperResult> WriteAsync(string address, float value, CancellationToken cancellationToken = default) => WriteAsync(address, value.ToString(), cancellationToken);
|
||||
/// <inheritdoc/>
|
||||
public override Task<OperResult> WriteAsync(string address, long value, CancellationToken cancellationToken = default) => WriteAsync(address, value.ToString(), cancellationToken);
|
||||
/// <inheritdoc/>
|
||||
public override Task<OperResult> WriteAsync(string address, ulong value, CancellationToken cancellationToken = default) => WriteAsync(address, value.ToString(), cancellationToken);
|
||||
/// <inheritdoc/>
|
||||
public override Task<OperResult> WriteAsync(string address, ushort value, CancellationToken cancellationToken = default) => WriteAsync(address, value.ToString(), cancellationToken);
|
||||
/// <inheritdoc/>
|
||||
public override Task<OperResult> WriteAsync(string address, short value, CancellationToken cancellationToken = default) => WriteAsync(address, value.ToString(), cancellationToken);
|
||||
/// <inheritdoc/>
|
||||
public override Task<OperResult> WriteAsync(string address, int value, CancellationToken cancellationToken = default) => WriteAsync(address, value.ToString(), cancellationToken);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override Task<OperResult> WriteAsync(string address, bool[] value, CancellationToken cancellationToken = default) => throw new NotImplementedException();
|
||||
|
||||
|
||||
#region 其他方法
|
||||
|
||||
/// <inheritdoc cref="DLT645_2007Util.BroadcastTime(IDLT645_2007,DateTime, CancellationToken)"/>
|
||||
public OperResult BroadcastTime(DateTime dateTime, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return DLT645_2007Util.BroadcastTime(this, dateTime, cancellationToken);
|
||||
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="DLT645_2007Util.FreezeAsync(IDLT645_2007,DateTime, CancellationToken)"/>
|
||||
public async Task<OperResult> FreezeAsync(DateTime dateTime, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return await DLT645_2007Util.FreezeAsync(this, dateTime, cancellationToken);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <inheritdoc cref="DLT645_2007Util.ReadDeviceStationAsync(IDLT645_2007, CancellationToken)"/>
|
||||
public async Task<OperResult<string>> ReadDeviceStationAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
return await DLT645_2007Util.ReadDeviceStationAsync(this, cancellationToken);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <inheritdoc cref="DLT645_2007Util.WriteBaudRateAsync(IDLT645_2007, int, CancellationToken)"/>
|
||||
public async Task<OperResult> WriteBaudRateAsync(int baudRate, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return await DLT645_2007Util.WriteBaudRateAsync(this, baudRate, cancellationToken);
|
||||
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="DLT645_2007Util.WriteDeviceStationAsync(IDLT645_2007, string, CancellationToken)"/>
|
||||
public async Task<OperResult> WriteDeviceStationAsync(string station, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return await DLT645_2007Util.WriteDeviceStationAsync(this, station, cancellationToken);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="DLT645_2007Util.WritePasswordAsync(IDLT645_2007, byte,string,string, CancellationToken)"/>
|
||||
public async Task<OperResult> WritePasswordAsync(byte level, string oldPassword, string newPassword, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return await DLT645_2007Util.WritePasswordAsync(this, level, oldPassword, newPassword, cancellationToken);
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,313 @@
|
||||
#region copyright
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
#endregion
|
||||
|
||||
using ThingsGateway.Foundation.Extension.Generic;
|
||||
using ThingsGateway.Foundation.Extension.String;
|
||||
|
||||
namespace ThingsGateway.Foundation.Adapter.DLT645;
|
||||
/// <summary>
|
||||
/// DLT645_2007
|
||||
/// </summary>
|
||||
internal static class DLT645_2007Util
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public static string GetAddressDescription()
|
||||
{
|
||||
var str = """
|
||||
查看附带文档或者相关资料,下面列举一下常见的数据标识地址
|
||||
|
||||
地址 说明
|
||||
-----------------------------------------
|
||||
02010100 A相电压
|
||||
02020100 A相电流
|
||||
02030000 瞬时总有功功率
|
||||
00000000 (当前)组合有功总电能
|
||||
00010000 (当前)正向有功总电能
|
||||
|
||||
""";
|
||||
return Environment.NewLine + str;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public static OperResult<byte[]> Read(IDLT645_2007 dlt645_2007, string address, int length, CancellationToken cancellationToken = default)
|
||||
{
|
||||
try
|
||||
{
|
||||
dlt645_2007.Connect(cancellationToken);
|
||||
var commandResult = DLT645Helper.GetDLT645_2007Command(address, (byte)ControlCode.Read, dlt645_2007.Station);
|
||||
if (!commandResult.IsSuccess) return commandResult;
|
||||
return dlt645_2007.SendThenReturn<DLT645_2007Message>(commandResult.Content, cancellationToken);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return new OperResult<byte[]>(ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public static async Task<OperResult<byte[]>> ReadAsync(IDLT645_2007 dlt645_2007, string address, int length, CancellationToken cancellationToken = default)
|
||||
{
|
||||
try
|
||||
{
|
||||
await dlt645_2007.ConnectAsync(cancellationToken);
|
||||
var commandResult = DLT645Helper.GetDLT645_2007Command(address, (byte)ControlCode.Read, dlt645_2007.Station);
|
||||
if (!commandResult.IsSuccess) return commandResult;
|
||||
return await dlt645_2007.SendThenReturnAsync<DLT645_2007Message>(commandResult.Content, cancellationToken);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return new OperResult<byte[]>(ex);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/// <inheritdoc/>
|
||||
public static OperResult Write(IDLT645_2007 dlt645_2007, string address, string value, CancellationToken cancellationToken = default)
|
||||
{
|
||||
|
||||
try
|
||||
{
|
||||
dlt645_2007.Connect(cancellationToken);
|
||||
dlt645_2007.Password ??= string.Empty;
|
||||
dlt645_2007.OperCode ??= string.Empty;
|
||||
if (dlt645_2007.Password.Length < 8)
|
||||
dlt645_2007.Password = dlt645_2007.Password.PadLeft(8, '0');
|
||||
if (dlt645_2007.OperCode.Length < 8)
|
||||
dlt645_2007.OperCode = dlt645_2007.OperCode.PadLeft(8, '0');
|
||||
|
||||
var data = DataTransUtil.SpliceArray(dlt645_2007.Password.ByHexStringToBytes(), dlt645_2007.OperCode.ByHexStringToBytes());
|
||||
string[] strArray = value.SplitStringBySemicolon();
|
||||
var commandResult = DLT645Helper.GetDLT645_2007Command(address, (byte)ControlCode.Write, dlt645_2007.Station, data, strArray);
|
||||
if (!commandResult.IsSuccess) return commandResult;
|
||||
return dlt645_2007.SendThenReturn<DLT645_2007Message>(commandResult.Content, cancellationToken);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return new OperResult<byte[]>(ex);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/// <inheritdoc/>
|
||||
public static async Task<OperResult> WriteAsync(IDLT645_2007 dlt645_2007, string address, string value, CancellationToken cancellationToken = default)
|
||||
{
|
||||
|
||||
try
|
||||
{
|
||||
await dlt645_2007.ConnectAsync(cancellationToken);
|
||||
dlt645_2007.Password ??= string.Empty;
|
||||
dlt645_2007.OperCode ??= string.Empty;
|
||||
if (dlt645_2007.Password.Length < 8)
|
||||
dlt645_2007.Password = dlt645_2007.Password.PadLeft(8, '0');
|
||||
if (dlt645_2007.OperCode.Length < 8)
|
||||
dlt645_2007.OperCode = dlt645_2007.OperCode.PadLeft(8, '0');
|
||||
var data = DataTransUtil.SpliceArray(dlt645_2007.Password.ByHexStringToBytes(), dlt645_2007.OperCode.ByHexStringToBytes());
|
||||
string[] strArray = value.SplitStringBySemicolon();
|
||||
var commandResult = DLT645Helper.GetDLT645_2007Command(address, (byte)ControlCode.Write, dlt645_2007.Station, data, strArray);
|
||||
if (!commandResult.IsSuccess) return commandResult;
|
||||
return await dlt645_2007.SendThenReturnAsync<DLT645_2007Message>(commandResult.Content, cancellationToken);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return new OperResult<byte[]>(ex);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#region 其他方法
|
||||
|
||||
/// <summary>
|
||||
/// 广播校时
|
||||
/// </summary>
|
||||
/// <param name="dlt645_2007">链路</param>
|
||||
/// <param name="dateTime">时间</param>
|
||||
/// <param name="cancellationToken">取消令箭</param>
|
||||
/// <returns></returns>
|
||||
public static OperResult BroadcastTime(IDLT645_2007 dlt645_2007, DateTime dateTime, CancellationToken cancellationToken = default)
|
||||
{
|
||||
try
|
||||
{
|
||||
dlt645_2007.Connect(cancellationToken);
|
||||
string str = $"{dateTime.Second:D2}{dateTime.Minute:D2}{dateTime.Hour:D2}{dateTime.Day:D2}{dateTime.Month:D2}{dateTime.Year % 100:D2}";
|
||||
var commandResult = DLT645Helper.GetDLT645_2007Command((byte)ControlCode.BroadcastTime, str.ByHexStringToBytes().ToArray(), "999999999999".ByHexStringToBytes());
|
||||
if (commandResult.IsSuccess)
|
||||
{
|
||||
dlt645_2007.Send(commandResult.Content);
|
||||
return OperResult.CreateSuccessResult();
|
||||
}
|
||||
else
|
||||
{
|
||||
return new OperResult(commandResult);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return new OperResult<string>(ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 冻结
|
||||
/// </summary>
|
||||
/// <param name="dlt645_2007">链路</param>
|
||||
/// <param name="dateTime">时间</param>
|
||||
/// <param name="cancellationToken">取消令箭</param>
|
||||
/// <returns></returns>
|
||||
public static async Task<OperResult> FreezeAsync(IDLT645_2007 dlt645_2007, DateTime dateTime, CancellationToken cancellationToken = default)
|
||||
{
|
||||
try
|
||||
{
|
||||
await dlt645_2007.ConnectAsync(cancellationToken);
|
||||
string str = $"{dateTime.Minute:D2}{dateTime.Hour:D2}{dateTime.Day:D2}{dateTime.Month:D2}";
|
||||
if (dlt645_2007.Station.IsNullOrEmpty()) dlt645_2007.Station = string.Empty;
|
||||
if (dlt645_2007.Station.Length < 12) dlt645_2007.Station = dlt645_2007.Station.PadLeft(12, '0');
|
||||
var commandResult = DLT645Helper.GetDLT645_2007Command((byte)ControlCode.Freeze, str.ByHexStringToBytes().ToArray(), dlt645_2007.Station.ByHexStringToBytes().Reverse().ToArray());
|
||||
if (!commandResult.IsSuccess) return commandResult;
|
||||
return await dlt645_2007.SendThenReturnAsync<DLT645_2007Message>(commandResult.Content, cancellationToken);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return new OperResult<string>(ex);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 读取通信地址
|
||||
/// </summary>
|
||||
/// <param name="dlt645_2007">链路</param>
|
||||
/// <param name="cancellationToken">取消令箭</param>
|
||||
/// <returns></returns>
|
||||
public static async Task<OperResult<string>> ReadDeviceStationAsync(IDLT645_2007 dlt645_2007, CancellationToken cancellationToken = default)
|
||||
{
|
||||
try
|
||||
{
|
||||
await dlt645_2007.ConnectAsync(cancellationToken);
|
||||
var commandResult = DLT645Helper.GetDLT645_2007Command((byte)ControlCode.ReadStation, null, "AAAAAAAAAAAA".ByHexStringToBytes());
|
||||
if (!commandResult.IsSuccess) return new(commandResult);
|
||||
var result = await dlt645_2007.SendThenReturnAsync<DLT645_2007Message>(commandResult.Content, cancellationToken);
|
||||
if (result.IsSuccess)
|
||||
{
|
||||
var buffer = result.Content.SelectMiddle(0, 6).BytesAdd(-0x33);
|
||||
return OperResult.CreateSuccessResult(buffer.Reverse().ToArray().ToHexString());
|
||||
}
|
||||
else
|
||||
{
|
||||
return new(result);
|
||||
}
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return new(ex);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 修改波特率
|
||||
/// </summary>
|
||||
/// <param name="dlt645_2007">链路</param>
|
||||
/// <param name="baudRate">波特率</param>
|
||||
/// <param name="cancellationToken">取消令箭</param>
|
||||
/// <returns></returns>
|
||||
public static async Task<OperResult> WriteBaudRateAsync(IDLT645_2007 dlt645_2007, int baudRate, CancellationToken cancellationToken = default)
|
||||
{
|
||||
try
|
||||
{
|
||||
await dlt645_2007.ConnectAsync(cancellationToken);
|
||||
byte baudRateByte;
|
||||
switch (baudRate)
|
||||
{
|
||||
case 600: baudRateByte = 0x02; break;
|
||||
case 1200: baudRateByte = 0x04; break;
|
||||
case 2400: baudRateByte = 0x08; break;
|
||||
case 4800: baudRateByte = 0x10; break;
|
||||
case 9600: baudRateByte = 0x20; break;
|
||||
case 19200: baudRateByte = 0x40; break;
|
||||
default: return new OperResult<string>($"不支持此波特率:{baudRate}");
|
||||
}
|
||||
if (dlt645_2007.Station.IsNullOrEmpty()) dlt645_2007.Station = string.Empty;
|
||||
if (dlt645_2007.Station.Length < 12) dlt645_2007.Station = dlt645_2007.Station.PadLeft(12, '0');
|
||||
var commandResult = DLT645Helper.GetDLT645_2007Command((byte)ControlCode.WriteBaudRate, new byte[] { baudRateByte }, dlt645_2007.Station.ByHexStringToBytes().Reverse().ToArray());
|
||||
if (!commandResult.IsSuccess) return commandResult;
|
||||
return await dlt645_2007.SendThenReturnAsync<DLT645_2007Message>(commandResult.Content, cancellationToken);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return new OperResult<string>(ex);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新通信地址
|
||||
/// </summary>
|
||||
/// <param name="dlt645_2007">链路</param>
|
||||
/// <param name="station">站号</param>
|
||||
/// <param name="cancellationToken">取消令箭</param>
|
||||
/// <returns></returns>
|
||||
public static async Task<OperResult> WriteDeviceStationAsync(IDLT645_2007 dlt645_2007, string station, CancellationToken cancellationToken = default)
|
||||
{
|
||||
try
|
||||
{
|
||||
await dlt645_2007.ConnectAsync(cancellationToken);
|
||||
var commandResult = DLT645Helper.GetDLT645_2007Command((byte)ControlCode.WriteStation, station.ByHexStringToBytes().Reverse().ToArray(), "AAAAAAAAAAAA".ByHexStringToBytes());
|
||||
if (!commandResult.IsSuccess) return commandResult;
|
||||
return await dlt645_2007.SendThenReturnAsync<DLT645_2007Message>(commandResult.Content, cancellationToken);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return new OperResult<string>(ex);
|
||||
}
|
||||
|
||||
}
|
||||
/// <summary>
|
||||
/// 修改密码
|
||||
/// </summary>
|
||||
/// <param name="dlt645_2007">链路</param>
|
||||
/// <param name="level">密码等级,0-8</param>
|
||||
/// <param name="oldPassword">旧密码</param>
|
||||
/// <param name="newPassword">新密码</param>
|
||||
/// <param name="cancellationToken">取消令箭</param>
|
||||
/// <returns></returns>
|
||||
public static async Task<OperResult> WritePasswordAsync(IDLT645_2007 dlt645_2007, byte level, string oldPassword, string newPassword, CancellationToken cancellationToken = default)
|
||||
{
|
||||
try
|
||||
{
|
||||
await dlt645_2007.ConnectAsync(cancellationToken);
|
||||
|
||||
if (dlt645_2007.Station.IsNullOrEmpty()) dlt645_2007.Station = string.Empty;
|
||||
if (dlt645_2007.Station.Length < 12) dlt645_2007.Station = dlt645_2007.Station.PadLeft(12, '0');
|
||||
string str = $"04000C{level + 1:D2}";
|
||||
|
||||
var commandResult = DLT645Helper.GetDLT645_2007Command((byte)ControlCode.WritePassword,
|
||||
str.ByHexStringToBytes().Reverse().ToArray()
|
||||
.SpliceArray(oldPassword.ByHexStringToBytes().Reverse().ToArray())
|
||||
.SpliceArray(newPassword.ByHexStringToBytes().Reverse().ToArray())
|
||||
, dlt645_2007.Station.ByHexStringToBytes().Reverse().ToArray());
|
||||
if (!commandResult.IsSuccess) return commandResult;
|
||||
return await dlt645_2007.SendThenReturnAsync<DLT645_2007Message>(commandResult.Content, cancellationToken);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return new OperResult<string>(ex);
|
||||
}
|
||||
|
||||
}
|
||||
#endregion
|
||||
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
#region copyright
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
#endregion
|
||||
|
||||
namespace ThingsGateway.Foundation.Adapter.DLT645
|
||||
{
|
||||
/// <summary>
|
||||
/// DLT645_2007
|
||||
/// </summary>
|
||||
public interface IDLT645_2007 : IReadWrite
|
||||
{
|
||||
/// <summary>
|
||||
/// 增加FE FE FE FE的报文头部
|
||||
/// </summary>
|
||||
bool EnableFEHead { get; set; }
|
||||
/// <summary>
|
||||
/// 操作员代码
|
||||
/// </summary>
|
||||
string OperCode { get; set; }
|
||||
/// <summary>
|
||||
/// 写入密码
|
||||
/// </summary>
|
||||
string Password { get; set; }
|
||||
/// <summary>
|
||||
/// 通讯地址BCD码,一般应该是12个字符
|
||||
/// </summary>
|
||||
string Station { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
#region copyright
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
#endregion
|
||||
|
||||
namespace ThingsGateway.Foundation.Adapter.DLT645;
|
||||
|
||||
internal static class PackHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// 打包变量,添加到<see href="deviceVariableSourceReads"></see>
|
||||
/// </summary>
|
||||
/// <param name="device"></param>
|
||||
/// <param name="deviceVariables"></param>
|
||||
/// <param name="maxPack">最大打包长度</param>
|
||||
/// <param name="defaultIntervalTime">默认间隔时间</param>
|
||||
/// <returns></returns>
|
||||
public static List<T> LoadSourceRead<T, T2>(IReadWrite device, List<T2> deviceVariables, int maxPack, int defaultIntervalTime) where T : IDeviceVariableSourceRead<IDeviceVariableRunTime>, new() where T2 : IDeviceVariableRunTime, new()
|
||||
{
|
||||
var byteConverter = device.ThingsGatewayBitConverter;
|
||||
var result = new List<T>();
|
||||
//需要先剔除额外信息,比如dataformat等
|
||||
foreach (var item in deviceVariables)
|
||||
{
|
||||
var address = item.Address;
|
||||
|
||||
IThingsGatewayBitConverter transformParameter = ByteTransformUtil.GetTransByAddress(ref address, byteConverter);
|
||||
item.ThingsGatewayBitConverter = transformParameter;
|
||||
//item.Address = address;
|
||||
item.Index = device.GetBitOffset(item.Address);
|
||||
|
||||
result.Add(new()
|
||||
{
|
||||
DeviceVariableRunTimes = new() { item },
|
||||
Address = address,
|
||||
Length = 1,
|
||||
IntervalTimeTick = new(item.IntervalTime ?? defaultIntervalTime)
|
||||
});
|
||||
}
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
#region copyright
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
#endregion
|
||||
|
||||
global using System;
|
||||
global using System.Collections.Generic;
|
||||
global using System.Linq;
|
||||
global using System.Threading;
|
||||
global using System.Threading.Tasks;
|
||||
|
||||
global using ThingsGateway.Foundation.Core;
|
||||
global using ThingsGateway.Foundation.Serial;
|
||||
global using ThingsGateway.Foundation.Sockets;
|
||||
@@ -0,0 +1,5 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\ThingsGateway.Foundation\ThingsGateway.Foundation.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,21 @@
|
||||
#region copyright
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
#endregion
|
||||
|
||||
global using System;
|
||||
global using System.Collections.Generic;
|
||||
global using System.Linq;
|
||||
global using System.Threading;
|
||||
global using System.Threading.Tasks;
|
||||
|
||||
global using ThingsGateway.Foundation.Core;
|
||||
global using ThingsGateway.Foundation.Serial;
|
||||
global using ThingsGateway.Foundation.Sockets;
|
||||
@@ -0,0 +1,233 @@
|
||||
#region copyright
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
#endregion
|
||||
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace ThingsGateway.Foundation.Adapter.Modbus;
|
||||
/// <inheritdoc/>
|
||||
public class ModbusTcpDtu : ReadWriteDevicesTcpServerBase
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public ModbusTcpDtu(TcpService tcpService) : base(tcpService)
|
||||
{
|
||||
ThingsGatewayBitConverter = new ThingsGatewayBitConverter(EndianType.Big);
|
||||
RegisterByteLength = 2;
|
||||
ModbusTcpDtuPlugin modbusTcpSalvePlugin = new ModbusTcpDtuPlugin();
|
||||
tcpService.Config.ConfigurePlugins(a =>
|
||||
{
|
||||
a.Add(modbusTcpSalvePlugin);
|
||||
});
|
||||
tcpService.Setup(tcpService.Config);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检测事务标识符
|
||||
/// </summary>
|
||||
[Description("检测事务标识符")]
|
||||
public bool IsCheckMessageId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 站号
|
||||
/// </summary>
|
||||
[Description("站号")]
|
||||
public byte Station { get; set; } = 1;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string GetAddressDescription()
|
||||
{
|
||||
return $"{base.GetAddressDescription()}{Environment.NewLine}{ModbusHelper.GetAddressDescription()}";
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override List<T> LoadSourceRead<T, T2>(List<T2> deviceVariables, int maxPack, int defaultIntervalTime)
|
||||
{
|
||||
return PackHelper.LoadSourceRead<T, T2>(this, deviceVariables, maxPack, defaultIntervalTime);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override OperResult<byte[]> Read(string address, int length, CancellationToken cancellationToken = default)
|
||||
{
|
||||
try
|
||||
{
|
||||
Connect(cancellationToken);
|
||||
var mAddress = ModbusAddress.ParseFrom(address, Station);
|
||||
var commandResult = ModbusHelper.GetReadModbusCommand(address, length, Station);
|
||||
if (!commandResult.IsSuccess) return commandResult;
|
||||
return SendThenReturn(mAddress.SocketId, commandResult.Content, cancellationToken);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return new OperResult<byte[]>(ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override async Task<OperResult<byte[]>> ReadAsync(string address, int length, CancellationToken cancellationToken = default)
|
||||
{
|
||||
try
|
||||
{
|
||||
await ConnectAsync(cancellationToken);
|
||||
var mAddress = ModbusAddress.ParseFrom(address, Station);
|
||||
var commandResult = ModbusHelper.GetReadModbusCommand(address, length, Station);
|
||||
if (!commandResult.IsSuccess) return commandResult;
|
||||
return await SendThenReturnAsync(mAddress.SocketId, commandResult.Content, cancellationToken);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return new OperResult<byte[]>(ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void SetDataAdapter(ISocketClient socketClient = default)
|
||||
{
|
||||
if (socketClient != default)
|
||||
{
|
||||
ModbusTcpDataHandleAdapter dataHandleAdapter = new()
|
||||
{
|
||||
IsCheckMessageId = IsCheckMessageId,
|
||||
CacheTimeout = TimeSpan.FromMilliseconds(CacheTimeout)
|
||||
};
|
||||
socketClient.SetDataHandlingAdapter(dataHandleAdapter);
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var item in TcpService.GetClients())
|
||||
{
|
||||
ModbusTcpDataHandleAdapter dataHandleAdapter = new()
|
||||
{
|
||||
IsCheckMessageId = IsCheckMessageId,
|
||||
CacheTimeout = TimeSpan.FromMilliseconds(CacheTimeout)
|
||||
};
|
||||
item.SetDataHandlingAdapter(dataHandleAdapter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override OperResult Write(string address, byte[] value, CancellationToken cancellationToken = default)
|
||||
{
|
||||
try
|
||||
{
|
||||
Connect(cancellationToken);
|
||||
var mAddress = ModbusAddress.ParseFrom(address, Station);
|
||||
var commandResult = ModbusHelper.GetWriteModbusCommand(address, value, Station);
|
||||
if (!commandResult.IsSuccess) return commandResult;
|
||||
return SendThenReturn(mAddress.SocketId, commandResult.Content, cancellationToken);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return new OperResult(ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override OperResult Write(string address, bool[] value, CancellationToken cancellationToken = default)
|
||||
{
|
||||
try
|
||||
{
|
||||
Connect(cancellationToken);
|
||||
var mAddress = ModbusAddress.ParseFrom(address, Station);
|
||||
var commandResult = ModbusHelper.GetWriteBoolModbusCommand(address, value, Station);
|
||||
if (!commandResult.IsSuccess) return commandResult;
|
||||
return SendThenReturn(mAddress.SocketId, commandResult.Content, cancellationToken);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return new OperResult(ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override async Task<OperResult> WriteAsync(string address, byte[] value, CancellationToken cancellationToken = default)
|
||||
{
|
||||
try
|
||||
{
|
||||
await ConnectAsync(cancellationToken);
|
||||
var mAddress = ModbusAddress.ParseFrom(address, Station);
|
||||
var commandResult = ModbusHelper.GetWriteModbusCommand(address, value, Station);
|
||||
if (!commandResult.IsSuccess) return commandResult;
|
||||
return await SendThenReturnAsync(mAddress.SocketId, commandResult.Content, cancellationToken);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return new OperResult(ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override async Task<OperResult> WriteAsync(string address, bool[] value, CancellationToken cancellationToken = default)
|
||||
{
|
||||
try
|
||||
{
|
||||
await ConnectAsync(cancellationToken);
|
||||
var mAddress = ModbusAddress.ParseFrom(address, Station);
|
||||
var commandResult = ModbusHelper.GetWriteBoolModbusCommand(address, value, Station);
|
||||
if (!commandResult.IsSuccess) return commandResult;
|
||||
return await SendThenReturnAsync(mAddress.SocketId, commandResult.Content, cancellationToken);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return new OperResult(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private OperResult<byte[]> SendThenReturn(string id, byte[] command, CancellationToken cancellationToken)
|
||||
{
|
||||
|
||||
if (TcpService.TryGetSocketClient($"ID={id}", out var client))
|
||||
{
|
||||
SetDataAdapter(client);
|
||||
return SendThenReturn<ModbusTcpMessage>(command, cancellationToken, client);
|
||||
}
|
||||
else
|
||||
{
|
||||
return new OperResult<byte[]>("客户端未连接");
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<OperResult<byte[]>> SendThenReturnAsync(string id, byte[] command, CancellationToken cancellationToken)
|
||||
{
|
||||
if (TcpService.TryGetSocketClient($"ID={id}", out var client))
|
||||
{
|
||||
SetDataAdapter(client);
|
||||
return await SendThenReturnAsync<ModbusTcpMessage>(command, cancellationToken, client);
|
||||
}
|
||||
else if (TcpService.SocketClients.Count == 1)
|
||||
{
|
||||
var client1 = TcpService.SocketClients.GetClients().FirstOrDefault();
|
||||
if (client1 != null)
|
||||
{
|
||||
SetDataAdapter(client1);
|
||||
return await SendThenReturnAsync<ModbusTcpMessage>(command, cancellationToken, client1);
|
||||
}
|
||||
}
|
||||
return new OperResult<byte[]>("客户端未连接");
|
||||
}
|
||||
|
||||
internal class ModbusTcpDtuPlugin : PluginBase, ITcpReceivingPlugin
|
||||
{
|
||||
public async Task OnTcpReceiving(ITcpClientBase client, ByteBlockEventArgs e)
|
||||
{
|
||||
if (client is ISocketClient socket)
|
||||
{
|
||||
if (!socket.Id.StartsWith("ID="))
|
||||
{
|
||||
ByteBlock byteBlock = e.ByteBlock;
|
||||
var id = $"ID={byteBlock.ToArray().ToHexString()}";
|
||||
socket.ResetId(id);
|
||||
}
|
||||
}
|
||||
await e.InvokeNext();//如果本插件无法处理当前数据,请将数据转至下一个插件。
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,174 @@
|
||||
#region copyright
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
#endregion
|
||||
|
||||
using System.Text;
|
||||
|
||||
using ThingsGateway.Foundation.Extension.String;
|
||||
|
||||
namespace ThingsGateway.Foundation.Adapter.Modbus;
|
||||
|
||||
/// <summary>
|
||||
/// Modbus协议地址
|
||||
/// </summary>
|
||||
public class ModbusAddress : DeviceAddressBase
|
||||
{
|
||||
/// <summary>
|
||||
/// <inheritdoc/>
|
||||
/// </summary>
|
||||
public ModbusAddress()
|
||||
{
|
||||
|
||||
}
|
||||
/// <summary>
|
||||
/// 读取功能码
|
||||
/// </summary>
|
||||
public ushort AddressStart => Address.ToUShort();
|
||||
/// <summary>
|
||||
/// 读取功能码
|
||||
/// </summary>
|
||||
public byte ReadFunction { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 站号信息
|
||||
/// </summary>
|
||||
public byte Station { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 写入功能码
|
||||
/// </summary>
|
||||
public byte WriteFunction { get; set; }
|
||||
/// <summary>
|
||||
/// 打包临时写入,需要读取的字节长度
|
||||
/// </summary>
|
||||
public int ByteLength { get; set; }
|
||||
/// <summary>
|
||||
/// BitIndex
|
||||
/// </summary>
|
||||
public int BitIndex => (int)(Address.SplitStringByDelimiter().LastOrDefault().ToInt());
|
||||
|
||||
/// <summary>
|
||||
/// 读取功能码
|
||||
/// </summary>
|
||||
public ushort AddressEnd => (ushort)(AddressStart + (Math.Ceiling(ByteLength / 2.0) > 0 ? Math.Ceiling(ByteLength / 2.0) : 1));
|
||||
|
||||
/// <summary>
|
||||
/// 作为Slave时需提供的SocketId,用于分辨Socket客户端,通常对比的是初始链接时的注册包
|
||||
/// </summary>
|
||||
public string SocketId { get; set; }
|
||||
/// <summary>
|
||||
/// 解析地址
|
||||
/// </summary>
|
||||
public static ModbusAddress ParseFrom(string address, byte station)
|
||||
{
|
||||
ModbusAddress modbusAddress = new()
|
||||
{
|
||||
Station = station
|
||||
};
|
||||
return ParseFrom(address, modbusAddress);
|
||||
}
|
||||
/// <summary>
|
||||
/// 解析地址
|
||||
/// </summary>
|
||||
public static ModbusAddress ParseFrom(string address, ModbusAddress modbusAddress = null)
|
||||
{
|
||||
modbusAddress ??= new();
|
||||
if (address.IndexOf(';') < 0)
|
||||
{
|
||||
Address(address);
|
||||
}
|
||||
else
|
||||
{
|
||||
string[] strArray = address.SplitStringBySemicolon();
|
||||
for (int index = 0; index < strArray.Length; ++index)
|
||||
{
|
||||
if (strArray[index].ToUpper().StartsWith("S="))
|
||||
{
|
||||
if (Convert.ToInt16(strArray[index].Substring(2)) > 0)
|
||||
modbusAddress.Station = byte.Parse(strArray[index].Substring(2));
|
||||
}
|
||||
else if (strArray[index].ToUpper().StartsWith("W="))
|
||||
{
|
||||
if (Convert.ToInt16(strArray[index].Substring(2)) > 0)
|
||||
modbusAddress.WriteFunction = byte.Parse(strArray[index].Substring(2));
|
||||
}
|
||||
else if (strArray[index].ToUpper().StartsWith("ID="))
|
||||
{
|
||||
modbusAddress.SocketId = strArray[index].Substring(3);
|
||||
}
|
||||
else if (!strArray[index].Contains("="))
|
||||
{
|
||||
Address(strArray[index]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return modbusAddress;
|
||||
|
||||
void Address(string address)
|
||||
{
|
||||
var readF = ushort.Parse(address.Substring(0, 1));
|
||||
if (readF > 4)
|
||||
throw new("功能码错误");
|
||||
switch (readF)
|
||||
{
|
||||
case 0:
|
||||
modbusAddress.ReadFunction = 1;
|
||||
break;
|
||||
case 1:
|
||||
modbusAddress.ReadFunction = 2;
|
||||
break;
|
||||
case 3:
|
||||
modbusAddress.ReadFunction = 4;
|
||||
break;
|
||||
case 4:
|
||||
modbusAddress.ReadFunction = 3;
|
||||
break;
|
||||
}
|
||||
modbusAddress.Address = (double.Parse(address.Substring(1)) - 1).ToString();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string ToString()
|
||||
{
|
||||
StringBuilder stringGeter = new();
|
||||
if (Station > 0)
|
||||
{
|
||||
stringGeter.Append($"s={Station.ToString()};");
|
||||
}
|
||||
if (WriteFunction > 0)
|
||||
{
|
||||
stringGeter.Append($"w={WriteFunction.ToString()};");
|
||||
}
|
||||
if (!string.IsNullOrEmpty(SocketId))
|
||||
{
|
||||
stringGeter.Append($"id={SocketId};");
|
||||
}
|
||||
stringGeter.Append(GetFunctionString(ReadFunction) + (AddressStart + 1).ToString());
|
||||
return stringGeter.ToString();
|
||||
}
|
||||
|
||||
private string GetFunctionString(int readF)
|
||||
{
|
||||
return readF switch
|
||||
{
|
||||
1 => "0",
|
||||
2 => "1",
|
||||
3 => "4",
|
||||
4 => "3",
|
||||
_ => "4",
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,334 @@
|
||||
#region copyright
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
#endregion
|
||||
|
||||
using System.Text;
|
||||
|
||||
using ThingsGateway.Foundation.Extension.Bool;
|
||||
using ThingsGateway.Foundation.Extension.Generic;
|
||||
|
||||
namespace ThingsGateway.Foundation.Adapter.Modbus;
|
||||
|
||||
internal class ModbusHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// 添加Crc16
|
||||
/// </summary>
|
||||
internal static byte[] AddCrc(byte[] command)
|
||||
{
|
||||
return CRC16Utils.CRC16(command);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 添加ModbusTcp报文头
|
||||
/// </summary>
|
||||
internal static byte[] AddModbusTcpHead(byte[] modbus, ushort id)
|
||||
{
|
||||
byte[] tcp = new byte[modbus.Length + 6];
|
||||
tcp[0] = BitConverter.GetBytes(id)[1];
|
||||
tcp[1] = BitConverter.GetBytes(id)[0];
|
||||
tcp[4] = BitConverter.GetBytes(modbus.Length)[1];
|
||||
tcp[5] = BitConverter.GetBytes(modbus.Length)[0];
|
||||
modbus.CopyTo(tcp, 6);
|
||||
return tcp;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// modbus地址格式说明
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
internal static string GetAddressDescription()
|
||||
{
|
||||
StringBuilder stringBuilder = new();
|
||||
stringBuilder.AppendLine("Modbus寄存器");
|
||||
stringBuilder.AppendLine("线圈寄存器使用从 00001 开始的地址编号。");
|
||||
stringBuilder.AppendLine("离散输入寄存器使用从 10001 开始的地址编号。");
|
||||
stringBuilder.AppendLine("输入寄存器使用从 30001 开始的地址编号。");
|
||||
stringBuilder.AppendLine("保持寄存器使用从 40001 开始的地址编号。");
|
||||
stringBuilder.AppendLine("举例:");
|
||||
stringBuilder.AppendLine("40001=>保持寄存器第一个寄存器");
|
||||
stringBuilder.AppendLine("额外格式:");
|
||||
stringBuilder.AppendLine("设备站号 ,比如40001;s=2; ,代表设备地址为2的保持寄存器第一个寄存器");
|
||||
stringBuilder.AppendLine("写入功能码 ,比如40001;w=16; ,代表保持寄存器第一个寄存器,写入值时采用0x10功能码,而不是默认的0x06功能码");
|
||||
return stringBuilder.ToString();
|
||||
}
|
||||
/// <summary>
|
||||
/// 通过错误码来获取到对应的文本消息
|
||||
/// </summary>
|
||||
internal static string GetDescriptionByErrorCode(byte code)
|
||||
{
|
||||
return code switch
|
||||
{
|
||||
1 => "不支持的功能码",
|
||||
2 => "读取寄存器越界",
|
||||
3 => "读取长度超限",
|
||||
4 => "读写异常",
|
||||
_ => "未知错误",
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取modbus数据区内容,返回数据需去除Crc和报文头,例如:01 03 02 00 01,发送数据需报文头
|
||||
/// </summary>
|
||||
/// <param name="send">发送数据</param>
|
||||
/// <param name="response">返回数据</param>
|
||||
/// <returns></returns>
|
||||
internal static OperResult<byte[], FilterResult> GetModbusData(byte[] send, byte[] response)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (response.Length < 3)
|
||||
return new OperResult<byte[], FilterResult>("数据长度不足" + response.ToHexString()) { Content2 = FilterResult.Cache };
|
||||
|
||||
|
||||
if (response[1] >= 0x80)//错误码
|
||||
return new OperResult<byte[], FilterResult>(GetDescriptionByErrorCode(response[2])) { Content2 = FilterResult.Success };
|
||||
if (response[1] <= 0x05)
|
||||
{
|
||||
if ((response.Length < response[2] + 3))
|
||||
return new OperResult<byte[], FilterResult>("数据长度不足" + response.ToHexString()) { Content2 = FilterResult.Cache };
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((response.Length < 6))
|
||||
return new OperResult<byte[], FilterResult>("数据长度不足" + response.ToHexString()) { Content2 = FilterResult.Cache };
|
||||
|
||||
}
|
||||
|
||||
if (send.Length == 0)
|
||||
{
|
||||
var result = OperResult.CreateSuccessResult(GenericExtensions.ArrayRemoveBegin(response, 3), FilterResult.Success);
|
||||
result.Message = "接收数据正确,但主机并没有主动请求数据";
|
||||
return result;
|
||||
}
|
||||
if (send[0] != response[0])
|
||||
return new OperResult<byte[], FilterResult>(string.Format("站号不一致", send[0], response[0])) { Content2 = FilterResult.Success };
|
||||
if (send[1] != response[1])
|
||||
return new OperResult<byte[], FilterResult>() { Message = "功能码不一致", Content2 = FilterResult.Success };
|
||||
return OperResult.CreateSuccessResult(GenericExtensions.ArrayRemoveBegin(response, 3), FilterResult.Success);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return new OperResult<byte[], FilterResult>(ex) { Content2 = FilterResult.Success };
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// 去除Crc,返回modbus数据区
|
||||
/// </summary>
|
||||
/// <param name="send"></param>
|
||||
/// <param name="response"></param>
|
||||
/// <param name="crcCheck"></param>
|
||||
/// <returns></returns>
|
||||
internal static OperResult<byte[], FilterResult> GetModbusRtuData(byte[] send, byte[] response, bool crcCheck = true)
|
||||
{
|
||||
if (response.Length < 3)
|
||||
return new OperResult<byte[], FilterResult>("数据长度不足" + response.ToHexString()) { Content2 = FilterResult.Cache };
|
||||
|
||||
if (response[1] >= 0x80)//错误码
|
||||
return new OperResult<byte[], FilterResult>(GetDescriptionByErrorCode(response[2])) { Content2 = FilterResult.Success };
|
||||
if (response[1] <= 0x05)
|
||||
{
|
||||
if ((response.Length < response[2] + 5))
|
||||
return new OperResult<byte[], FilterResult>("数据长度不足" + response.ToHexString()) { Content2 = FilterResult.Cache };
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((response.Length < 8))
|
||||
return new OperResult<byte[], FilterResult>("数据长度不足" + response.ToHexString()) { Content2 = FilterResult.Cache };
|
||||
|
||||
}
|
||||
|
||||
|
||||
var data = response.SelectMiddle(0, response[2] != 0 ? response[2] + 5 : 8);
|
||||
if (crcCheck && !CRC16Utils.CheckCRC16(data))
|
||||
return new OperResult<byte[], FilterResult>("Crc校验失败" + DataTransUtil.ByteToHexString(data, ' ')) { Content2 = FilterResult.Success };
|
||||
return GetModbusData(send, data.RemoveLast(2));
|
||||
}
|
||||
/// <summary>
|
||||
/// 获取读取报文
|
||||
/// </summary>
|
||||
internal static OperResult<byte[]> GetReadModbusCommand(string address, int length, byte station)
|
||||
{
|
||||
try
|
||||
{
|
||||
var mAddress = ModbusAddress.ParseFrom(address, station);
|
||||
return OperResult.CreateSuccessResult(GetReadModbusCommand(mAddress, length));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return new OperResult<byte[]>(ex);
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// 获取写入布尔量报文,根据地址识别功能码
|
||||
/// </summary>
|
||||
internal static OperResult<byte[]> GetWriteBoolModbusCommand(string address, bool[] values, byte station)
|
||||
{
|
||||
try
|
||||
{
|
||||
var mAddress = ModbusAddress.ParseFrom(address, station);
|
||||
|
||||
//功能码或实际长度
|
||||
if (values?.Length > 1 || mAddress.WriteFunction == 15)
|
||||
return GetWriteBoolModbusCommand(mAddress, values, values.Length);
|
||||
else
|
||||
return GetWriteBoolModbusCommand(address, values[0], station);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return new OperResult<byte[]>(ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取写入字报文,根据地址识别功能码
|
||||
/// </summary>
|
||||
internal static OperResult<byte[]> GetWriteModbusCommand(string address, byte[] value, byte station)
|
||||
{
|
||||
try
|
||||
{
|
||||
var mAddress = ModbusAddress.ParseFrom(address, station);
|
||||
|
||||
//功能码或实际长度
|
||||
if (value?.Length > 2 || mAddress.WriteFunction == 16)
|
||||
return OperResult.CreateSuccessResult(GetWriteModbusCommand(mAddress, value));
|
||||
else
|
||||
return OperResult.CreateSuccessResult(GetWriteOneModbusCommand(mAddress, value));
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return new OperResult<byte[]>(ex);
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// 获取读取报文
|
||||
/// </summary>
|
||||
internal static byte[] GetReadModbusCommand(ModbusAddress mAddress, int length)
|
||||
{
|
||||
byte[] array = new byte[6]
|
||||
{
|
||||
(byte) mAddress.Station,
|
||||
(byte) mAddress.ReadFunction,
|
||||
BitConverter.GetBytes(mAddress.AddressStart)[1],
|
||||
BitConverter.GetBytes(mAddress.AddressStart)[0],
|
||||
BitConverter.GetBytes(length)[1],
|
||||
BitConverter.GetBytes(length)[0]
|
||||
};
|
||||
return array;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取05写入布尔量报文
|
||||
/// </summary>
|
||||
internal static OperResult<byte[]> GetWriteBoolModbusCommand(string address, bool value, byte station)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (address.IndexOf('.') <= 0)
|
||||
{
|
||||
var mAddress = ModbusAddress.ParseFrom(address, station);
|
||||
|
||||
return OperResult.CreateSuccessResult(GetWriteBoolModbusCommand(mAddress, value));
|
||||
}
|
||||
return new("不支持写入字寄存器的某一位");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return new OperResult<byte[]>(ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取05写入布尔量报文
|
||||
/// </summary>
|
||||
private static byte[] GetWriteBoolModbusCommand(ModbusAddress mAddress, bool value)
|
||||
{
|
||||
byte[] array = new byte[6]
|
||||
{
|
||||
(byte) mAddress.Station,
|
||||
(byte)5,
|
||||
BitConverter.GetBytes(mAddress.AddressStart)[1],
|
||||
BitConverter.GetBytes(mAddress.AddressStart)[0],
|
||||
0,
|
||||
0
|
||||
};
|
||||
if (value)
|
||||
{
|
||||
array[4] = 0xFF;
|
||||
array[5] = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
array[4] = 0;
|
||||
array[5] = 0;
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取15写入布尔量报文
|
||||
/// </summary>
|
||||
internal static OperResult<byte[]> GetWriteBoolModbusCommand(ModbusAddress mAddress, bool[] values, int length)
|
||||
{
|
||||
try
|
||||
{
|
||||
byte[] numArray1 = values.BoolArrayToByte();
|
||||
byte[] numArray2 = new byte[7 + numArray1.Length];
|
||||
numArray2[0] = (byte)mAddress.Station;
|
||||
numArray2[1] = (byte)15;
|
||||
numArray2[2] = BitConverter.GetBytes(mAddress.AddressStart)[1];
|
||||
numArray2[3] = BitConverter.GetBytes(mAddress.AddressStart)[0];
|
||||
numArray2[4] = (byte)(length / 256);
|
||||
numArray2[5] = (byte)(length % 256);
|
||||
numArray2[6] = (byte)numArray1.Length;
|
||||
numArray1.CopyTo(numArray2, 7);
|
||||
return OperResult.CreateSuccessResult(numArray2);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return new OperResult<byte[]>(ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取16写入字报文
|
||||
/// </summary>
|
||||
internal static byte[] GetWriteModbusCommand(ModbusAddress mAddress, byte[] values)
|
||||
{
|
||||
byte[] numArray = new byte[7 + values.Length];
|
||||
numArray[0] = (byte)mAddress.Station;
|
||||
numArray[1] = (byte)16;
|
||||
numArray[2] = BitConverter.GetBytes(mAddress.AddressStart)[1];
|
||||
numArray[3] = BitConverter.GetBytes(mAddress.AddressStart)[0];
|
||||
numArray[4] = (byte)(values.Length / 2 / 256);
|
||||
numArray[5] = (byte)(values.Length / 2 % 256);
|
||||
numArray[6] = (byte)values.Length;
|
||||
values.CopyTo(numArray, 7);
|
||||
return numArray;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取6写入字报文
|
||||
/// </summary>
|
||||
internal static byte[] GetWriteOneModbusCommand(ModbusAddress mAddress, byte[] values)
|
||||
{
|
||||
byte[] numArray = new byte[4 + values.Length];
|
||||
numArray[0] = (byte)mAddress.Station;
|
||||
numArray[1] = (byte)6;
|
||||
numArray[2] = BitConverter.GetBytes(mAddress.AddressStart)[1];
|
||||
numArray[3] = BitConverter.GetBytes(mAddress.AddressStart)[0];
|
||||
values.CopyTo(numArray, 4);
|
||||
return numArray;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
#region copyright
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
#endregion
|
||||
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace ThingsGateway.Foundation.Adapter.Modbus;
|
||||
/// <summary>
|
||||
/// ModbusRtu
|
||||
/// </summary>
|
||||
public class ModbusRtu : ReadWriteDevicesSerialSessionBase
|
||||
{
|
||||
/// <summary>
|
||||
/// ModbusRtu
|
||||
/// </summary>
|
||||
/// <param name="serialSession"></param>
|
||||
public ModbusRtu(SerialSession serialSession) : base(serialSession)
|
||||
{
|
||||
ThingsGatewayBitConverter = new ThingsGatewayBitConverter(EndianType.Big);
|
||||
RegisterByteLength = 2;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Crc校验
|
||||
/// </summary>
|
||||
[Description("Crc校验")]
|
||||
public bool Crc16CheckEnable { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// 站号
|
||||
/// </summary>
|
||||
[Description("站号")]
|
||||
public byte Station { get; set; } = 1;
|
||||
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string GetAddressDescription()
|
||||
{
|
||||
return $"{base.GetAddressDescription()}{Environment.NewLine}{ModbusHelper.GetAddressDescription()}";
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override List<T> LoadSourceRead<T, T2>(List<T2> deviceVariables, int maxPack, int defaultIntervalTime)
|
||||
{
|
||||
return PackHelper.LoadSourceRead<T, T2>(this, deviceVariables, maxPack, defaultIntervalTime);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override OperResult<byte[]> Read(string address, int length, CancellationToken cancellationToken = default)
|
||||
{
|
||||
try
|
||||
{
|
||||
Connect(cancellationToken);
|
||||
|
||||
return ModbusHelper.GetReadModbusCommand(address, length, Station).Then(a => SendThenReturn<ModbusRtuMessage>(a, cancellationToken));
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return new OperResult<byte[]>(ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override async Task<OperResult<byte[]>> ReadAsync(string address, int length, CancellationToken cancellationToken = default)
|
||||
{
|
||||
try
|
||||
{
|
||||
await ConnectAsync(cancellationToken);
|
||||
var commandResult = ModbusHelper.GetReadModbusCommand(address, length, Station);
|
||||
if (!commandResult.IsSuccess) return commandResult;
|
||||
return await SendThenReturnAsync<ModbusRtuMessage>(commandResult.Content, cancellationToken);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return new OperResult<byte[]>(ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void SetDataAdapter(ISocketClient socketClient = default)
|
||||
{
|
||||
ModbusRtuDataHandleAdapter dataHandleAdapter = new()
|
||||
{
|
||||
Crc16CheckEnable = Crc16CheckEnable,
|
||||
CacheTimeout = TimeSpan.FromMilliseconds(CacheTimeout)
|
||||
};
|
||||
SerialSession.SetDataHandlingAdapter(dataHandleAdapter);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override OperResult Write(string address, byte[] value, CancellationToken cancellationToken = default)
|
||||
{
|
||||
try
|
||||
{
|
||||
Connect(cancellationToken);
|
||||
var commandResult = ModbusHelper.GetWriteModbusCommand(address, value, Station);
|
||||
if (!commandResult.IsSuccess) return commandResult;
|
||||
return SendThenReturn<ModbusRtuMessage>(commandResult.Content, cancellationToken);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return new OperResult(ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override OperResult Write(string address, bool[] value, CancellationToken cancellationToken = default)
|
||||
{
|
||||
try
|
||||
{
|
||||
Connect(cancellationToken);
|
||||
var commandResult = ModbusHelper.GetWriteBoolModbusCommand(address, value, Station);
|
||||
if (!commandResult.IsSuccess) return commandResult;
|
||||
return SendThenReturn<ModbusRtuMessage>(commandResult.Content, cancellationToken);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return new OperResult(ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override async Task<OperResult> WriteAsync(string address, byte[] value, CancellationToken cancellationToken = default)
|
||||
{
|
||||
try
|
||||
{
|
||||
await ConnectAsync(cancellationToken);
|
||||
var commandResult = ModbusHelper.GetWriteModbusCommand(address, value, Station);
|
||||
if (!commandResult.IsSuccess) return commandResult;
|
||||
return await SendThenReturnAsync<ModbusRtuMessage>(commandResult.Content, cancellationToken);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return new OperResult(ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override async Task<OperResult> WriteAsync(string address, bool[] value, CancellationToken cancellationToken = default)
|
||||
{
|
||||
try
|
||||
{
|
||||
await ConnectAsync(cancellationToken);
|
||||
var commandResult = ModbusHelper.GetWriteBoolModbusCommand(address, value, Station);
|
||||
if (!commandResult.IsSuccess) return commandResult;
|
||||
return await SendThenReturnAsync<ModbusRtuMessage>(commandResult.Content, cancellationToken);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return new OperResult(ex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
#region copyright
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
#endregion
|
||||
|
||||
using ThingsGateway.Foundation.Extension.Generic;
|
||||
|
||||
namespace ThingsGateway.Foundation.Adapter.Modbus;
|
||||
|
||||
/// <summary>
|
||||
/// Rtu适配器
|
||||
/// </summary>
|
||||
internal class ModbusRtuDataHandleAdapter : ReadWriteDevicesSingleStreamDataHandleAdapter<ModbusRtuMessage>
|
||||
{
|
||||
/// <summary>
|
||||
/// 检测CRC
|
||||
/// </summary>
|
||||
public bool Crc16CheckEnable { get; set; } = true;
|
||||
/// <summary>
|
||||
/// <inheritdoc/>
|
||||
/// </summary>
|
||||
/// <param name="command"></param>
|
||||
/// <returns></returns>
|
||||
public override byte[] PackCommand(byte[] command)
|
||||
{
|
||||
return ModbusHelper.AddCrc(command);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override ModbusRtuMessage GetInstance()
|
||||
{
|
||||
return new ModbusRtuMessage();
|
||||
}
|
||||
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override FilterResult UnpackResponse(ModbusRtuMessage request, byte[] send, byte[] body, byte[] response)
|
||||
{
|
||||
//链路干扰时需剔除前缀中的多于字节,初步按站号+功能码找寻初始字节
|
||||
if (send?.Length > 0)
|
||||
{
|
||||
int index = -1;
|
||||
for (int i = 0; i < response.Length - 1; i++)
|
||||
{
|
||||
if (response[i] == send[0] && (response[i + 1] == send[1] || response[i + 1] == (send[1] + 0x80)))
|
||||
{
|
||||
index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (index >= 0)
|
||||
{
|
||||
response = response.RemoveBegin(index);
|
||||
}
|
||||
|
||||
//理想状态检测
|
||||
var result = ModbusHelper.GetModbusRtuData(send, response, Crc16CheckEnable);
|
||||
if (result.IsSuccess)
|
||||
{
|
||||
request.ErrorCode = result.ErrorCode;
|
||||
request.Message = result.Message;
|
||||
request.Content = result.Content;
|
||||
}
|
||||
else
|
||||
{
|
||||
request.ErrorCode = result.ErrorCode;
|
||||
request.Message = result.Message;
|
||||
}
|
||||
return result.Content2;
|
||||
}
|
||||
else
|
||||
{
|
||||
return FilterResult.Success;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
#region copyright
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
#endregion
|
||||
|
||||
namespace ThingsGateway.Foundation.Adapter.Modbus;
|
||||
/// <summary>
|
||||
/// <inheritdoc/>
|
||||
/// </summary>
|
||||
internal class ModbusRtuMessage : MessageBase, IMessage
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public override int HeadBytesLength => -1;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool CheckHeadBytes(byte[] heads)
|
||||
{
|
||||
BodyLength = -1;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
#region copyright
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
#endregion
|
||||
|
||||
using System.ComponentModel;
|
||||
|
||||
|
||||
namespace ThingsGateway.Foundation.Adapter.Modbus;
|
||||
/// <inheritdoc/>
|
||||
public class ModbusRtuOverTcp : ReadWriteDevicesTcpClientBase
|
||||
{
|
||||
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ModbusRtuOverTcp(TcpClient tcpClient) : base(tcpClient)
|
||||
{
|
||||
ThingsGatewayBitConverter = new ThingsGatewayBitConverter(EndianType.Big);
|
||||
RegisterByteLength = 2;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Crc校验
|
||||
/// </summary>
|
||||
[Description("Crc校验")]
|
||||
public bool Crc16CheckEnable { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// 站号
|
||||
/// </summary>
|
||||
[Description("站号")]
|
||||
public byte Station { get; set; } = 1;
|
||||
/// <inheritdoc/>
|
||||
public override string GetAddressDescription()
|
||||
{
|
||||
return $"{base.GetAddressDescription()}{Environment.NewLine}{ModbusHelper.GetAddressDescription()}";
|
||||
}
|
||||
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override OperResult<byte[]> Read(string address, int length, CancellationToken cancellationToken = default)
|
||||
{
|
||||
try
|
||||
{
|
||||
Connect(cancellationToken);
|
||||
var commandResult = ModbusHelper.GetReadModbusCommand(address, length, Station);
|
||||
if (!commandResult.IsSuccess) return commandResult;
|
||||
return SendThenReturn<ModbusRtuMessage>(commandResult.Content, cancellationToken);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return new OperResult<byte[]>(ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override async Task<OperResult<byte[]>> ReadAsync(string address, int length, CancellationToken cancellationToken = default)
|
||||
{
|
||||
try
|
||||
{
|
||||
await ConnectAsync(cancellationToken);
|
||||
var commandResult = ModbusHelper.GetReadModbusCommand(address, length, Station);
|
||||
if (!commandResult.IsSuccess) return commandResult;
|
||||
return await SendThenReturnAsync<ModbusRtuMessage>(commandResult.Content, cancellationToken);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return new OperResult<byte[]>(ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override List<T> LoadSourceRead<T, T2>(List<T2> deviceVariables, int maxPack, int defaultIntervalTime)
|
||||
{
|
||||
return PackHelper.LoadSourceRead<T, T2>(this, deviceVariables, maxPack, defaultIntervalTime);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void SetDataAdapter(ISocketClient socketClient = default)
|
||||
{
|
||||
ModbusRtuDataHandleAdapter dataHandleAdapter = new()
|
||||
{
|
||||
Crc16CheckEnable = Crc16CheckEnable,
|
||||
CacheTimeout = TimeSpan.FromMilliseconds(CacheTimeout)
|
||||
};
|
||||
TcpClient.SetDataHandlingAdapter(dataHandleAdapter);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override OperResult Write(string address, byte[] value, CancellationToken cancellationToken = default)
|
||||
{
|
||||
try
|
||||
{
|
||||
Connect(cancellationToken);
|
||||
var commandResult = ModbusHelper.GetWriteModbusCommand(address, value, Station);
|
||||
if (!commandResult.IsSuccess) return commandResult;
|
||||
return SendThenReturn<ModbusRtuMessage>(commandResult.Content, cancellationToken);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return new OperResult(ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override OperResult Write(string address, bool[] value, CancellationToken cancellationToken = default)
|
||||
{
|
||||
try
|
||||
{
|
||||
Connect(cancellationToken);
|
||||
var commandResult = ModbusHelper.GetWriteBoolModbusCommand(address, value, Station);
|
||||
if (!commandResult.IsSuccess) return commandResult;
|
||||
return SendThenReturn<ModbusRtuMessage>(commandResult.Content, cancellationToken);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return new OperResult(ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override async Task<OperResult> WriteAsync(string address, byte[] value, CancellationToken cancellationToken = default)
|
||||
{
|
||||
try
|
||||
{
|
||||
await ConnectAsync(cancellationToken);
|
||||
var commandResult = ModbusHelper.GetWriteModbusCommand(address, value, Station);
|
||||
if (!commandResult.IsSuccess) return commandResult;
|
||||
return await SendThenReturnAsync<ModbusRtuMessage>(commandResult.Content, cancellationToken);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return new OperResult(ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override async Task<OperResult> WriteAsync(string address, bool[] value, CancellationToken cancellationToken = default)
|
||||
{
|
||||
try
|
||||
{
|
||||
await ConnectAsync(cancellationToken);
|
||||
var commandResult = ModbusHelper.GetWriteBoolModbusCommand(address, value, Station);
|
||||
if (!commandResult.IsSuccess) return commandResult;
|
||||
return await SendThenReturnAsync<ModbusRtuMessage>(commandResult.Content, cancellationToken);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return new OperResult(ex);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
#region copyright
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
#endregion
|
||||
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace ThingsGateway.Foundation.Adapter.Modbus;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public class ModbusRtuOverUdp : ReadWriteDevicesUdpSessionBase
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public ModbusRtuOverUdp(UdpSession udpSession) : base(udpSession)
|
||||
{
|
||||
ThingsGatewayBitConverter = new ThingsGatewayBitConverter(EndianType.Big);
|
||||
RegisterByteLength = 2;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Crc校验
|
||||
/// </summary>
|
||||
[Description("Crc校验")]
|
||||
public bool Crc16CheckEnable { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 站号
|
||||
/// </summary>
|
||||
[Description("站号")]
|
||||
public byte Station { get; set; } = 1;
|
||||
/// <inheritdoc/>
|
||||
public override string GetAddressDescription()
|
||||
{
|
||||
return $"{base.GetAddressDescription()}{Environment.NewLine}{ModbusHelper.GetAddressDescription()}";
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public override OperResult<byte[]> Read(string address, int length, CancellationToken cancellationToken = default)
|
||||
{
|
||||
try
|
||||
{
|
||||
Connect(cancellationToken);
|
||||
var commandResult = ModbusHelper.GetReadModbusCommand(address, length, Station);
|
||||
if (!commandResult.IsSuccess) return commandResult;
|
||||
return SendThenReturn<ModbusRtuMessage>(commandResult.Content, cancellationToken);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return new OperResult<byte[]>(ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override async Task<OperResult<byte[]>> ReadAsync(string address, int length, CancellationToken cancellationToken = default)
|
||||
{
|
||||
try
|
||||
{
|
||||
await ConnectAsync(cancellationToken);
|
||||
var commandResult = ModbusHelper.GetReadModbusCommand(address, length, Station);
|
||||
if (!commandResult.IsSuccess) return commandResult;
|
||||
return await SendThenReturnAsync<ModbusRtuMessage>(commandResult.Content, cancellationToken);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return new OperResult<byte[]>(ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void SetDataAdapter(ISocketClient socketClient = default)
|
||||
{
|
||||
ModbusRtuOverUdpDataHandleAdapter dataHandleAdapter = new()
|
||||
{
|
||||
Crc16CheckEnable = Crc16CheckEnable,
|
||||
};
|
||||
UdpSession.Config.SetUdpDataHandlingAdapter(() =>
|
||||
{
|
||||
return dataHandleAdapter;
|
||||
});
|
||||
UdpSession.Setup(UdpSession.Config);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override List<T> LoadSourceRead<T, T2>(List<T2> deviceVariables, int maxPack, int defaultIntervalTime)
|
||||
{
|
||||
return PackHelper.LoadSourceRead<T, T2>(this, deviceVariables, maxPack, defaultIntervalTime);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override OperResult Write(string address, byte[] value, CancellationToken cancellationToken = default)
|
||||
{
|
||||
try
|
||||
{
|
||||
Connect(cancellationToken);
|
||||
var commandResult = ModbusHelper.GetWriteModbusCommand(address, value, Station);
|
||||
if (!commandResult.IsSuccess) return commandResult;
|
||||
return SendThenReturn<ModbusRtuMessage>(commandResult.Content, cancellationToken);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return new OperResult(ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override OperResult Write(string address, bool[] value, CancellationToken cancellationToken = default)
|
||||
{
|
||||
try
|
||||
{
|
||||
Connect(cancellationToken);
|
||||
var commandResult = ModbusHelper.GetWriteBoolModbusCommand(address, value, Station);
|
||||
if (!commandResult.IsSuccess) return commandResult;
|
||||
return SendThenReturn<ModbusRtuMessage>(commandResult.Content, cancellationToken);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return new OperResult(ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override async Task<OperResult> WriteAsync(string address, byte[] value, CancellationToken cancellationToken = default)
|
||||
{
|
||||
try
|
||||
{
|
||||
await ConnectAsync(cancellationToken);
|
||||
var commandResult = ModbusHelper.GetWriteModbusCommand(address, value, Station);
|
||||
if (!commandResult.IsSuccess) return commandResult;
|
||||
return await SendThenReturnAsync<ModbusRtuMessage>(commandResult.Content, cancellationToken);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return new OperResult(ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override async Task<OperResult> WriteAsync(string address, bool[] value, CancellationToken cancellationToken = default)
|
||||
{
|
||||
try
|
||||
{
|
||||
await ConnectAsync(cancellationToken);
|
||||
var commandResult = ModbusHelper.GetWriteBoolModbusCommand(address, value, Station);
|
||||
if (!commandResult.IsSuccess) return commandResult;
|
||||
return await SendThenReturnAsync<ModbusRtuMessage>(commandResult.Content, cancellationToken);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return new OperResult(ex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
#region copyright
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
#endregion
|
||||
|
||||
namespace ThingsGateway.Foundation.Adapter.Modbus;
|
||||
|
||||
/// <summary>
|
||||
/// <inheritdoc/>
|
||||
/// </summary>
|
||||
internal class ModbusRtuOverUdpDataHandleAdapter : ReadWriteDevicesUdpDataHandleAdapter<ModbusRtuMessage>
|
||||
{
|
||||
/// <summary>
|
||||
/// 检测CRC
|
||||
/// </summary>
|
||||
public bool Crc16CheckEnable { get; set; } = true;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override byte[] PackCommand(byte[] command)
|
||||
{
|
||||
return ModbusHelper.AddCrc(command);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override ModbusRtuMessage GetInstance()
|
||||
{
|
||||
return new ModbusRtuMessage();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override OperResult<byte[]> UnpackResponse(byte[] send, byte[] response)
|
||||
{
|
||||
var result = ModbusHelper.GetModbusRtuData(send, response, Crc16CheckEnable);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user