KupaKuper_EthernetWrapper 2.2.5
dotnet add package KupaKuper_EthernetWrapper --version 2.2.5
NuGet\Install-Package KupaKuper_EthernetWrapper -Version 2.2.5
<PackageReference Include="KupaKuper_EthernetWrapper" Version="2.2.5" />
<PackageVersion Include="KupaKuper_EthernetWrapper" Version="2.2.5" />
<PackageReference Include="KupaKuper_EthernetWrapper" />
paket add KupaKuper_EthernetWrapper --version 2.2.5
#r "nuget: KupaKuper_EthernetWrapper, 2.2.5"
#:package KupaKuper_EthernetWrapper@2.2.5
#addin nuget:?package=KupaKuper_EthernetWrapper&version=2.2.5
#tool nuget:?package=KupaKuper_EthernetWrapper&version=2.2.5
BaseEthernet 类使用文档
概述
BaseEthernet 是一个抽象类,为以太网通信提供基础功能,主要用于与 PLC 等设备进行连接、数据读写和心跳检测。该类定义了连接管理、数据读写的标准接口,子类需实现具体的通信协议(如 OPC UA、Modbus TCP 等)。
命名空间: KupaKuper_EthernetWrapper.Ethernet
功能特性
- 连接管理: 自动重连机制、连接状态监控
- 数据读写: 支持多种数据类型、批量读写操作
- 心跳检测: 自动心跳检测确保连接稳定性
- 线程安全: 内置锁机制防止并发冲突
- 错误处理: 完善的异常处理和重试机制
类结构详解
连接管理
属性
Connected
public bool Connected { get; protected set; }
- 描述: 获取当前连接状态,设置时触发
ConnectChanged事件 - 线程安全: 使用锁机制确保线程安全
- 访问权限: 读/写(protected set)
ReconnectTime
public int ReconnectTime = 1000;
- 描述: 重连间隔时间(毫秒),默认值为 1000
- 类型:
int - 访问权限: 读/写
方法
Reconnect()
public virtual async Task Reconnect()
- 描述: 重新连接设备,包含锁定机制防止重复重连
- 返回值:
Task - 访问权限: public virtual
- 实现逻辑:
- 检查是否正在重连,避免重复操作
- 关闭当前连接
- 建立新连接
- 重置重连状态
CloseAsync()
public abstract Task CloseAsync()
- 描述: 关闭连接,子类必须实现
- 返回值:
Task - 访问权限: public abstract
ConnectAsync()
public abstract Task ConnectAsync()
- 描述: 连接 PLC,子类必须实现
- 返回值:
Task - 访问权限: public abstract
事件
ConnectChanged
public Action<bool>? ConnectChanged { get; set; }
- 描述: 连接状态改变时触发的事件
- 参数:
bool- 新的连接状态(true 表示已连接,false 表示断开连接) - 使用示例:
ethernet.ConnectChanged += (isConnected) =>
{
if (isConnected)
{
Console.WriteLine("设备连接成功");
// 连接成功后的处理逻辑
}
else
{
Console.WriteLine("设备连接断开");
// 连接断开后的处理逻辑
}
};
数据读写
抽象方法(子类必须实现)
ReadNode<T>(string Adr)
public abstract T? ReadNode<T>(string Adr)
- 描述: 读取变量地址的底层方法
- 类型参数:
T- 返回值类型 - 参数:
Adr- 变量地址 - 返回值:
T?- 读取的值,可为 null
ReadNodes<T>(List<string> Adrs)
public abstract T? ReadNodes<T>(List<string> Adrs)
- 描述: 读取变量地址列表的底层方法
- 类型参数:
T- 返回值类型 - 参数:
Adrs- 变量地址列表 - 返回值:
T?- 读取的值,可为 null
WriteNode(string trg, object value)
public abstract void WriteNode(string trg, object value)
- 描述: 写入变量地址的底层方法
- 参数:
trg- 目标变量地址value- 要写入的值
虚方法(可重写)
Read(string trg)
public virtual object? Read(string trg)
- 描述: 读取单个变量内容,处理数组类型转换
- 参数:
trg- 变量地址 - 返回值:
object?- 读取的值,可为 null - 功能特性:
- 自动检测连接状态
- 支持数组类型自动转换
- 处理空值情况
Read(List<string> trgs)
public virtual object? Read(List<string> trgs)
- 描述: 读取一列变量内容
- 参数:
trgs- 变量地址列表 - 返回值:
object?- 读取的值,可为 null
Read<T>(string trg)
public virtual T? Read<T>(string trg)
- 描述: 读取指定类型的变量
- 类型参数:
T- 返回值类型 - 参数:
trg- 变量地址 - 返回值:
T?- 读取的值,可为 null
ReadString(string trg, string encoding = "UTF-8")
public virtual string ReadString(string trg, string encoding = "UTF-8")
- 描述: 读取字符串变量并指定编码格式
- 参数:
trg- 变量地址encoding- 编码格式,默认为 "UTF-8"
- 返回值:
string- 读取的字符串 - 支持编码: UTF-8、GB2312、ASCII 等
Write(string trg, object value)
public virtual void Write(string trg, object value)
- 描述: 修改变量内容
- 参数:
trg- 变量地址value- 要写入的值
Try 方法(安全操作)
TryRead(string trg, out object? value)
public bool TryRead(string trg, out object? value)
- 描述: 尝试读取变量,返回是否成功
- 参数:
trg- 变量地址value- 输出参数,读取的值
- 返回值:
bool- 是否成功读取
TryRead(List<string> trg, out object? value)
public bool TryRead(List<string> trg, out object? value)
- 描述: 尝试读取一列变量,返回是否成功
- 参数:
trg- 变量地址列表value- 输出参数,读取的值
- 返回值:
bool- 是否成功读取
TryReadString(string trg, string encoding, out object? value)
public bool TryReadString(string trg, string encoding, out object? value)
- 描述: 尝试读取字符串变量,返回是否成功
- 参数:
trg- 变量地址encoding- 编码格式value- 输出参数,读取的值
- 返回值:
bool- 是否成功读取
TryRead<T>(string trg, out T? value)
public bool TryRead<T>(string trg, out T? value)
- 描述: 尝试读取指定类型变量,返回值和成功状态
- 类型参数:
T- 返回值类型 - 参数:
trg- 变量地址value- 输出参数,读取的值
- 返回值:
bool- 是否成功读取
TryWrite(string trg, object value)
public bool TryWrite(string trg, object value)
- 描述: 尝试修改变量内容,返回是否成功
- 参数:
trg- 变量地址value- 要写入的值
- 返回值:
bool- 是否成功写入
心跳检测
字段
_heartbeatWriteState
private bool _heartbeatWriteState = true;
- 描述: 心跳写入状态跟踪变量(布尔类型)
- 类型:
bool - 初始值: true
_heartbeatNumericState
private int _heartbeatNumericState = 1;
- 描述: 心跳写入状态跟踪变量(数值类型)
- 类型:
int - 初始值: 1
_isHeartbeatBoolType
private bool _isHeartbeatBoolType = true;
- 描述: 心跳类型标志,默认为 bool 类型
- 类型:
bool - 初始值: true
方法
OpenConnect(string ClientUrl, string HeartbeatAddress)
public void OpenConnect(string ClientUrl, string HeartbeatAddress)
- 描述: 启动连接监控和心跳检测
- 参数:
ClientUrl- 客户端 URL 地址HeartbeatAddress- 心跳检测变量地址
- 功能特性:
- 自动检测连接状态
- 定时执行心跳检测
- 自动重连机制
- 智能数据类型检测
使用说明
创建子类实现
要使用 BaseEthernet,首先需要创建一个继承自它的子类,并实现所有抽象方法:
using KupaKuper_EthernetWrapper.Ethernet;
public class MyEthernet : BaseEthernet
{
private readonly object _connectionLock = new object();
private bool _isConnected = false;
public MyEthernet()
{
// 初始化逻辑
}
public override async Task CloseAsync()
{
lock (_connectionLock)
{
if (!_isConnected) return;
// 实现关闭连接的具体逻辑
// 例如:关闭网络连接、释放资源等
_isConnected = false;
Connected = false; // 触发连接状态变化事件
}
await Task.CompletedTask;
}
public override async Task ConnectAsync()
{
lock (_connectionLock)
{
if (_isConnected) return;
try
{
// 实现连接逻辑
// 例如:建立网络连接、验证身份等
_isConnected = true;
Connected = true; // 触发连接状态变化事件
}
catch (Exception ex)
{
_isConnected = false;
Connected = false;
throw new Exception($"连接失败: {ex.Message}");
}
}
await Task.CompletedTask;
}
public override T? ReadNode<T>(string Adr)
{
if (!Connected) return default;
try
{
// 实现单个节点读取的具体逻辑
// 根据具体协议实现数据读取
// 示例:返回模拟数据
if (typeof(T) == typeof(bool))
return (T)(object)true;
else if (typeof(T) == typeof(int))
return (T)(object)123;
else if (typeof(T) == typeof(string))
return (T)(object)"Hello World";
return default;
}
catch (Exception ex)
{
throw new Exception($"读取节点失败: {ex.Message}");
}
}
public override T? ReadNodes<T>(List<string> Adrs)
{
if (!Connected) return default;
try
{
// 实现多节点读取的具体逻辑
// 根据具体协议实现批量数据读取
// 示例:返回模拟数据数组
if (typeof(T) == typeof(bool[]))
return (T)(object)new bool[] { true, false, true };
else if (typeof(T) == typeof(int[]))
return (T)(object)new int[] { 1, 2, 3 };
return default;
}
catch (Exception ex)
{
throw new Exception($"批量读取节点失败: {ex.Message}");
}
}
public override void WriteNode(string trg, object value)
{
if (!Connected) return;
try
{
// 实现写入节点的具体逻辑
// 根据具体协议实现数据写入
Console.WriteLine($"写入地址 {trg} 值: {value}");
}
catch (Exception ex)
{
throw new Exception($"写入节点失败: {ex.Message}");
}
}
}
初始化与连接
// 创建实例
var ethernet = new MyEthernet();
// 设置重连间隔(毫秒)
ethernet.ReconnectTime = 2000; // 2秒
// 注册连接状态变化事件
ethernet.ConnectChanged += (isConnected) =>
{
if (isConnected)
{
Console.WriteLine("设备连接成功");
// 连接成功后的初始化操作
InitializeAfterConnection();
}
else
{
Console.WriteLine("设备连接断开");
// 连接断开后的清理操作
CleanupAfterDisconnection();
}
};
// 启动连接监控和心跳检测
string clientUrl = "192.168.1.100:502"; // PLC地址
string heartbeatAddress = "DB1.DBX0.0"; // 心跳变量地址
ethernet.OpenConnect(clientUrl, heartbeatAddress);
// 手动连接(可选)
await ethernet.ConnectAsync();
数据读写操作
读取数据
// 读取布尔值
bool? boolValue = ethernet.Read<bool>("DB1.DBX0.0");
if (boolValue.HasValue)
{
Console.WriteLine($"布尔值: {boolValue.Value}");
}
// 读取整数
int? intValue = ethernet.Read<int>("DB1.DBW2.0");
if (intValue.HasValue)
{
Console.WriteLine($"整数值: {intValue.Value}");
}
// 读取浮点数
float? floatValue = ethernet.Read<float>("DB1.DBD4.0");
if (floatValue.HasValue)
{
Console.WriteLine($"浮点值: {floatValue.Value}");
}
// 读取字符串
string strValue = ethernet.ReadString("DB1.DBB8.0", "UTF-8");
Console.WriteLine($"字符串值: {strValue}");
// 读取数组
int[]? arrayValue = ethernet.Read<int[]>("DB1.DBB12.0");
if (arrayValue != null)
{
Console.WriteLine($"数组值: {string.Join(", ", arrayValue)}");
}
// 安全读取(推荐)
if (ethernet.TryRead<int>("DB1.DBW2.0", out int safeIntValue))
{
Console.WriteLine($"安全读取整数值: {safeIntValue}");
}
else
{
Console.WriteLine("读取失败");
}
// 批量读取
var addresses = new List<string>
{
"DB1.DBX0.0",
"DB1.DBW2.0",
"DB1.DBD4.0"
};
if (ethernet.TryRead(addresses, out object? batchResult))
{
Console.WriteLine($"批量读取结果: {batchResult}");
}
写入数据
// 写入布尔值
ethernet.Write("DB1.DBX0.0", true);
// 写入整数
ethernet.Write("DB1.DBW2.0", 123);
// 写入浮点数
ethernet.Write("DB1.DBD4.0", 3.14f);
// 写入字符串
string message = "Hello PLC";
byte[] stringBytes = Encoding.UTF8.GetBytes(message);
ethernet.Write("DB1.DBB8.0", stringBytes);
// 写入数组
int[] dataArray = { 1, 2, 3, 4, 5 };
ethernet.Write("DB1.DBB12.0", dataArray);
// 安全写入(推荐)
if (ethernet.TryWrite("DB1.DBW2.0", 456))
{
Console.WriteLine("写入成功");
}
else
{
Console.WriteLine("写入失败");
}
高级用法
自定义心跳检测
// 自定义心跳检测逻辑
public class CustomEthernet : BaseEthernet
{
public override void OpenConnect(string ClientUrl, string HeartbeatAddress)
{
// 自定义心跳检测间隔
ReconnectTime = 3000; // 3秒
// 调用基类实现
base.OpenConnect(ClientUrl, HeartbeatAddress);
}
// 其他实现...
}
批量操作优化
// 批量读写优化示例
public async Task BatchOperationExample()
{
var ethernet = new MyEthernet();
// 批量读取多个变量
var readAddresses = new List<string>
{
"DB1.DBX0.0",
"DB1.DBW2.0",
"DB1.DBD4.0",
"DB1.DBB8.0"
};
if (ethernet.TryRead(readAddresses, out object? readResults))
{
// 处理批量读取结果
ProcessBatchResults(readResults);
}
// 批量写入多个变量
var writeOperations = new Dictionary<string, object>
{
["DB1.DBX0.0"] = true,
["DB1.DBW2.0"] = 100,
["DB1.DBD4.0"] = 2.718f
};
foreach (var operation in writeOperations)
{
if (ethernet.TryWrite(operation.Key, operation.Value))
{
Console.WriteLine($"写入 {operation.Key} 成功");
}
}
}
错误处理与重试机制
public async Task RobustOperationExample()
{
var ethernet = new MyEthernet();
// 带重试的读取操作
int retryCount = 3;
for (int i = 0; i < retryCount; i++)
{
try
{
if (ethernet.TryRead<int>("DB1.DBW2.0", out int value))
{
Console.WriteLine($"读取成功: {value}");
break;
}
}
catch (Exception ex)
{
Console.WriteLine($"第 {i + 1} 次读取失败: {ex.Message}");
if (i == retryCount - 1)
{
// 最后一次重试失败,执行重连
await ethernet.Reconnect();
}
await Task.Delay(1000); // 等待1秒后重试
}
}
}
最佳实践
1. 连接管理
- 连接检查: 在执行操作前检查
Connected状态 - 事件处理: 合理使用
ConnectChanged事件处理连接状态变化 - 资源释放: 及时调用
CloseAsync()释放资源
2. 数据读写
- 安全操作: 优先使用
Try系列方法进行安全操作 - 批量优化: 对多个相关变量使用批量读写操作
- 类型匹配: 确保读写的数据类型与变量定义匹配
3. 错误处理
- 异常捕获: 对关键操作进行异常捕获
- 重试机制: 实现合理的重试逻辑
- 日志记录: 记录重要的操作和错误信息
4. 性能优化
- 连接复用: 避免频繁创建和销毁连接
- 批量操作: 减少单次操作次数
- 心跳优化: 根据实际需求调整心跳检测间隔
常见问题
Q: 连接状态不更新怎么办?
A: 确保在子类的 ConnectAsync() 和 CloseAsync() 方法中正确设置 Connected 属性
Q: 数据读取返回 null 是什么原因?
A: 可能的原因包括:连接断开、变量地址错误、权限不足、数据类型不匹配等
Q: 如何实现自定义协议?
A: 创建继承自 BaseEthernet 的子类,实现所有抽象方法,根据具体协议实现通信逻辑
Q: 心跳检测失败如何处理?
A: 心跳检测失败会自动触发重连机制,也可以通过 ConnectChanged 事件进行自定义处理
Q: 如何优化大量数据的读写性能?
A: 使用批量读写方法,合理设置操作间隔,避免频繁的小数据包传输
版本信息
- 当前版本: 1.0
- 目标框架: .NET Standard 2.0+
- 依赖项: KupaKuper_HelpClass
- 兼容性: 支持多种以太网通信协议
本文档基于 BaseEthernet 类实际实现编写,具体使用请参考实际代码和具体协议实现。
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- KupaKuper_HelpClass (>= 1.5.5)
- NModbus (>= 3.0.81)
- OPCFoundation.NetStandard.Opc.Ua (>= 1.5.378.106)
- OPCFoundation.NetStandard.Opc.Ua.Client (>= 1.5.378.106)
-
net8.0
- KupaKuper_HelpClass (>= 1.5.5)
- NModbus (>= 3.0.81)
- OPCFoundation.NetStandard.Opc.Ua (>= 1.5.378.106)
- OPCFoundation.NetStandard.Opc.Ua.Client (>= 1.5.378.106)
NuGet packages (1)
Showing the top 1 NuGet packages that depend on KupaKuper_EthernetWrapper:
| Package | Downloads |
|---|---|
|
KupaKuper_HMI_Config
KupaKuper_HMI_Device的支持包,主要包含配置文件读取和解析,提供数据模型和结构 |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 2.2.5 | 563 | 7/13/2026 |
| 2.2.4 | 411 | 4/21/2026 |
| 2.2.3 | 444 | 3/24/2026 |
| 2.2.2 | 171 | 3/21/2026 |
| 2.2.1-beta | 193 | 3/16/2026 |
| 2.1.1 | 279 | 2/28/2026 |
| 2.0.1 | 567 | 8/28/2025 |
| 2.0.0 | 329 | 8/26/2025 |
| 1.2.1 | 271 | 8/26/2025 |
| 1.2.0 | 350 | 8/25/2025 |
| 1.2.0-beta | 224 | 8/22/2025 |
| 1.1.2-beta | 201 | 8/2/2025 |
| 1.1.0-beta | 123 | 8/2/2025 |
| 1.0.0 | 292 | 7/28/2025 |
对于Opc不能直接读取WString类型的问题,可以支持使用ReadString来读取byte数组实现Wstring的读取