diff --git a/Modbus.Net/Modbus.Net.BigEndian3412/Modbus.Net.BigEndian3412ValueHelper.cs b/Modbus.Net/Modbus.Net.BigEndian3412/Modbus.Net.BigEndian3412ValueHelper.cs
index 859d78c..a960385 100644
--- a/Modbus.Net/Modbus.Net.BigEndian3412/Modbus.Net.BigEndian3412ValueHelper.cs
+++ b/Modbus.Net/Modbus.Net.BigEndian3412/Modbus.Net.BigEndian3412ValueHelper.cs
@@ -2,49 +2,158 @@ using System;
namespace Modbus.Net
{
+ ///
+ /// 端格式扩展 / Endianness Extensions
+ ///
+ /// 定义特殊的字节序格式
+ /// Defines special byte order formats
+ ///
+ /// BigEndian3412 和 LittleEndian3412 是特殊的字节序格式
+ /// BigEndian3412 and LittleEndian3412 are special byte order formats
+ ///
+ ///
+ ///
public partial class Endian
{
+ ///
+ /// 大端 3412 格式 / Big Endian 3412 Format
+ ///
+ /// 值:10
+ /// Value: 10
+ ///
+ /// 字节序:3-4-1-2
+ /// Byte order: 3-4-1-2
+ ///
+ ///
+ ///
public const int BigEndian3412 = 10;
+ ///
+ /// 小端 3412 格式 / Little Endian 3412 Format
+ ///
+ /// 值:11
+ /// Value: 11
+ ///
+ /// 字节序:3-4-1-2 (小端基础)
+ /// Byte order: 3-4-1-2 (little-endian base)
+ ///
+ ///
+ ///
public const int LittleEndian3412 = 11;
}
+ #region 大端 3412 值辅助类 / Big Endian 3412 Value Helper
+
+ ///
+ /// 大端 3412 字节序值辅助类 / Big Endian 3412 Byte Order Value Helper Class
+ ///
+ /// 实现特殊的 3412 字节序转换,继承自大端 LSB 辅助类
+ /// Implements special 3412 byte order conversion, inherits from Big Endian LSB helper
+ ///
+ /// 字节序说明 / Byte Order Description:
+ ///
+ /// - 32 位整数:字节 3-4-1-2 / 32-bit integer: bytes 3-4-1-2
+ /// - 64 位整数:字节 7-8-5-6-3-4-1-2 / 64-bit integer: bytes 7-8-5-6-3-4-1-2
+ ///
+ ///
+ ///
+ /// 使用场景 / Use Cases:
+ ///
+ /// - 某些特殊工业设备 / Some special industrial devices
+ /// - 自定义协议格式 / Custom protocol formats
+ ///
+ ///
+ ///
+ /// 使用示例 / Usage Example:
+ ///
+ /// // 获取大端 3412 实例 / Get BigEndian3412 instance
+ /// var helper = BigEndian3412ValueHelper.Instance;
+ ///
+ /// // 从字节数组读取 32 位整数 / Read 32-bit integer from byte array
+ /// byte[] data = [0x12, 0x34, 0x56, 0x78];
+ /// int pos = 0;
+ /// int value = helper.GetInt(data, ref pos);
+ /// // 字节序:56-78-12-34 → 0x56781234
+ ///
+ ///
+ ///
+ ///
public class BigEndian3412ValueHelper : BigEndianLsbValueHelper
{
private static BigEndian3412ValueHelper _bigEndian3412Instance;
///
- /// 构造器
+ /// 构造函数 / Constructor
+ ///
+ /// 保护的构造函数,防止外部实例化
+ /// Protected constructor to prevent external instantiation
+ ///
///
protected BigEndian3412ValueHelper()
{
}
///
- /// 覆写的实例获取
+ /// 实例获取 / Instance Get
+ ///
+ /// 重写基类的实例获取方法
+ /// Overrides base class instance get method
+ ///
///
protected override ValueHelper _Instance => _bigEndian3412Instance;
///
- /// 是否为大端
+ /// 是否为大端 / Whether Big Endian
+ ///
+ /// 返回 true (实际上是基于大端的 3412 变体)
+ /// Returns true (actually a 3412 variant based on big-endian)
+ ///
///
public override bool LittleEndian => true;
+ ///
+ /// 是否为小端位 / Whether Little Endian Bit
+ ///
+ /// 返回 false
+ /// Returns false
+ ///
+ ///
public override bool LittleEndianBit => false;
///
- /// 覆盖的获取实例的方法
+ /// 获取实例 / Get Instance
+ ///
+ /// 单例模式获取实例
+ /// Singleton pattern to get instance
+ ///
///
public new static BigEndian3412ValueHelper Instance
=> _bigEndian3412Instance ?? (_bigEndian3412Instance = new BigEndian3412ValueHelper());
+ ///
+ /// 获取 32 位整数 / Get 32-bit Integer
+ ///
+ /// 按 3412 字节序读取 32 位整数
+ /// Read 32-bit integer in 3412 byte order
+ ///
+ /// 字节序:3-4-1-2
+ /// Byte order: 3-4-1-2
+ ///
+ ///
+ ///
+ /// 字节数组 / Byte Array
+ /// 当前位置 (引用传递) / Current Position (passed by reference)
+ /// 32 位整数值 / 32-bit Integer Value
public override int GetInt(byte[] data, ref int pos)
{
Array.Reverse(data, pos, 4);
byte temp;
+ // 交换字节:3-4-1-2
+ // Swap bytes: 3-4-1-2
temp = data[pos]; data[pos] = data[pos + 2]; data[pos + 2] = temp;
temp = data[pos + 1]; data[pos + 1] = data[pos + 3]; data[pos + 3] = temp;
var t = BitConverter.ToInt32(data, pos);
+ // 恢复原状 / Restore original
temp = data[pos]; data[pos] = data[pos + 2]; data[pos + 2] = temp;
temp = data[pos + 1]; data[pos + 1] = data[pos + 3]; data[pos + 3] = temp;
Array.Reverse(data, pos, 4);
@@ -52,6 +161,16 @@ namespace Modbus.Net
return t;
}
+ ///
+ /// 获取 32 位无符号整数 / Get 32-bit Unsigned Integer
+ ///
+ /// 按 3412 字节序读取 32 位无符号整数
+ /// Read 32-bit unsigned integer in 3412 byte order
+ ///
+ ///
+ /// 字节数组 / Byte Array
+ /// 当前位置 / Current Position
+ /// 32 位无符号整数值 / 32-bit Unsigned Integer Value
public override uint GetUInt(byte[] data, ref int pos)
{
Array.Reverse(data, pos, 4);
@@ -66,6 +185,20 @@ namespace Modbus.Net
return t;
}
+ ///
+ /// 获取 64 位整数 / Get 64-bit Integer
+ ///
+ /// 按 3412 字节序读取 64 位整数
+ /// Read 64-bit integer in 3412 byte order
+ ///
+ /// 字节序:7-8-5-6-3-4-1-2
+ /// Byte order: 7-8-5-6-3-4-1-2
+ ///
+ ///
+ ///
+ /// 字节数组 / Byte Array
+ /// 当前位置 / Current Position
+ /// 64 位整数值 / 64-bit Integer Value
public override long GetLong(byte[] data, ref int pos)
{
Array.Reverse(data, pos, 8);
@@ -84,6 +217,16 @@ namespace Modbus.Net
return t;
}
+ ///
+ /// 获取 64 位无符号整数 / Get 64-bit Unsigned Integer
+ ///
+ /// 按 3412 字节序读取 64 位无符号整数
+ /// Read 64-bit unsigned integer in 3412 byte order
+ ///
+ ///
+ /// 字节数组 / Byte Array
+ /// 当前位置 / Current Position
+ /// 64 位无符号整数值 / 64-bit Unsigned Integer Value
public override ulong GetULong(byte[] data, ref int pos)
{
Array.Reverse(data, pos, 8);
@@ -102,6 +245,16 @@ namespace Modbus.Net
return t;
}
+ ///
+ /// 获取 32 位浮点数 / Get 32-bit Float
+ ///
+ /// 按 3412 字节序读取 32 位浮点数
+ /// Read 32-bit float in 3412 byte order
+ ///
+ ///
+ /// 字节数组 / Byte Array
+ /// 当前位置 / Current Position
+ /// 32 位浮点数值 / 32-bit Float Value
public override float GetFloat(byte[] data, ref int pos)
{
Array.Reverse(data, pos, 4);
@@ -116,6 +269,16 @@ namespace Modbus.Net
return t;
}
+ ///
+ /// 获取 64 位浮点数 / Get 64-bit Double
+ ///
+ /// 按 3412 字节序读取 64 位浮点数
+ /// Read 64-bit float in 3412 byte order
+ ///
+ ///
+ /// 字节数组 / Byte Array
+ /// 当前位置 / Current Position
+ /// 64 位浮点数值 / 64-bit Float Value
public override double GetDouble(byte[] data, ref int pos)
{
Array.Reverse(data, pos, 8);
@@ -135,35 +298,86 @@ namespace Modbus.Net
}
}
+ #endregion
+
+ #region 小端 3412 值辅助类 / Little Endian 3412 Value Helper
+
+ ///
+ /// 小端 3412 字节序值辅助类 / Little Endian 3412 Byte Order Value Helper Class
+ ///
+ /// 实现特殊的小端 3412 字节序转换,继承自小端 LSB 辅助类
+ /// Implements special little-endian 3412 byte order conversion, inherits from Little Endian LSB helper
+ ///
+ /// 字节序说明 / Byte Order Description:
+ ///
+ /// - 32 位整数:字节 3-4-1-2 (小端基础) / 32-bit integer: bytes 3-4-1-2 (little-endian base)
+ /// - 64 位整数:字节 7-8-5-6-3-4-1-2 (小端基础) / 64-bit integer: bytes 7-8-5-6-3-4-1-2 (little-endian base)
+ ///
+ ///
+ ///
+ ///
public class LittleEndian3412ValueHelper : LittleEndianLsbValueHelper
{
private static LittleEndian3412ValueHelper _littleEndian3412Instance;
///
- /// 构造器
+ /// 构造函数 / Constructor
+ ///
+ /// 保护的构造函数,防止外部实例化
+ /// Protected constructor to prevent external instantiation
+ ///
///
protected LittleEndian3412ValueHelper()
{
}
///
- /// 覆写的实例获取
+ /// 实例获取 / Instance Get
+ ///
+ /// 重写基类的实例获取方法
+ /// Overrides base class instance get method
+ ///
///
protected override ValueHelper _Instance => _littleEndian3412Instance;
///
- /// 是否为大端
+ /// 是否为大端 / Whether Big Endian
+ ///
+ /// 返回 true (实际上是小端 3412 变体)
+ /// Returns true (actually a little-endian 3412 variant)
+ ///
///
public override bool LittleEndian => true;
+ ///
+ /// 是否为小端位 / Whether Little Endian Bit
+ ///
+ /// 返回 true
+ /// Returns true
+ ///
+ ///
public override bool LittleEndianBit => true;
///
- /// 覆盖的获取实例的方法
+ /// 获取实例 / Get Instance
+ ///
+ /// 单例模式获取实例
+ /// Singleton pattern to get instance
+ ///
///
public new static LittleEndian3412ValueHelper Instance
=> _littleEndian3412Instance ?? (_littleEndian3412Instance = new LittleEndian3412ValueHelper());
+ ///
+ /// 获取 32 位整数 / Get 32-bit Integer
+ ///
+ /// 按小端 3412 字节序读取 32 位整数
+ /// Read 32-bit integer in little-endian 3412 byte order
+ ///
+ ///
+ /// 字节数组 / Byte Array
+ /// 当前位置 / Current Position
+ /// 32 位整数值 / 32-bit Integer Value
public override int GetInt(byte[] data, ref int pos)
{
byte temp;
@@ -176,6 +390,16 @@ namespace Modbus.Net
return t;
}
+ ///
+ /// 获取 32 位无符号整数 / Get 32-bit Unsigned Integer
+ ///
+ /// 按小端 3412 字节序读取 32 位无符号整数
+ /// Read 32-bit unsigned integer in little-endian 3412 byte order
+ ///
+ ///
+ /// 字节数组 / Byte Array
+ /// 当前位置 / Current Position
+ /// 32 位无符号整数值 / 32-bit Unsigned Integer Value
public override uint GetUInt(byte[] data, ref int pos)
{
byte temp;
@@ -188,6 +412,16 @@ namespace Modbus.Net
return t;
}
+ ///
+ /// 获取 64 位整数 / Get 64-bit Integer
+ ///
+ /// 按小端 3412 字节序读取 64 位整数
+ /// Read 64-bit integer in little-endian 3412 byte order
+ ///
+ ///
+ /// 字节数组 / Byte Array
+ /// 当前位置 / Current Position
+ /// 64 位整数值 / 64-bit Integer Value
public override long GetLong(byte[] data, ref int pos)
{
byte temp;
@@ -204,6 +438,16 @@ namespace Modbus.Net
return t;
}
+ ///
+ /// 获取 64 位无符号整数 / Get 64-bit Unsigned Integer
+ ///
+ /// 按小端 3412 字节序读取 64 位无符号整数
+ /// Read 64-bit unsigned integer in little-endian 3412 byte order
+ ///
+ ///
+ /// 字节数组 / Byte Array
+ /// 当前位置 / Current Position
+ /// 64 位无符号整数值 / 64-bit Unsigned Integer Value
public override ulong GetULong(byte[] data, ref int pos)
{
byte temp;
@@ -220,6 +464,16 @@ namespace Modbus.Net
return t;
}
+ ///
+ /// 获取 32 位浮点数 / Get 32-bit Float
+ ///
+ /// 按小端 3412 字节序读取 32 位浮点数
+ /// Read 32-bit float in little-endian 3412 byte order
+ ///
+ ///
+ /// 字节数组 / Byte Array
+ /// 当前位置 / Current Position
+ /// 32 位浮点数值 / 32-bit Float Value
public override float GetFloat(byte[] data, ref int pos)
{
byte temp;
@@ -232,6 +486,16 @@ namespace Modbus.Net
return t;
}
+ ///
+ /// 获取 64 位浮点数 / Get 64-bit Double
+ ///
+ /// 按小端 3412 字节序读取 64 位浮点数
+ /// Read 64-bit float in little-endian 3412 byte order
+ ///
+ ///
+ /// 字节数组 / Byte Array
+ /// 当前位置 / Current Position
+ /// 64 位浮点数值 / 64-bit Float Value
public override double GetDouble(byte[] data, ref int pos)
{
byte temp;
@@ -248,4 +512,6 @@ namespace Modbus.Net
return t;
}
}
-}
\ No newline at end of file
+
+ #endregion
+}
diff --git a/Modbus.Net/Modbus.Net.CodeGenerator/ConnectorWithControllerByteArrayCodeGenerator.cs b/Modbus.Net/Modbus.Net.CodeGenerator/ConnectorWithControllerByteArrayCodeGenerator.cs
index 74dcb4f..39f69d1 100644
--- a/Modbus.Net/Modbus.Net.CodeGenerator/ConnectorWithControllerByteArrayCodeGenerator.cs
+++ b/Modbus.Net/Modbus.Net.CodeGenerator/ConnectorWithControllerByteArrayCodeGenerator.cs
@@ -1,11 +1,43 @@
-using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis;
using System.Text;
namespace Modbus.Net.CodeGenerator
{
+ ///
+ /// BaseConnector 代码生成器 / BaseConnector Code Generator
+ ///
+ /// 使用 Roslyn 源生成器自动生成 BaseConnector 的部分代码
+ /// Automatically generates partial code for BaseConnector using Roslyn source generator
+ ///
+ /// 生成内容 / Generated Content:
+ ///
+ /// - 发送锁属性 / Send lock property
+ /// - 全双工属性 / Full-duplex property
+ /// - 超时时间属性 / Timeout property
+ /// - SendMsgAsync 方法实现 / SendMsgAsync method implementation
+ ///
+ ///
+ ///
+ /// 使用场景 / Use Cases:
+ ///
+ /// - 减少重复代码 / Reduce repetitive code
+ /// - 统一的发送逻辑 / Unified send logic
+ /// - 编译时生成,无运行时开销 / Compile-time generation, no runtime overhead
+ ///
+ ///
+ ///
+ ///
[Generator]
public class BaseConnectorCodeGenerator : ISourceGenerator
{
+ ///
+ /// 执行代码生成 / Execute Code Generation
+ ///
+ /// 生成 BaseConnector 的部分类代码
+ /// Generate partial class code for BaseConnector
+ ///
+ ///
+ /// 生成器执行上下文 / Generator Execution Context
public void Execute(GeneratorExecutionContext context)
{
var source = $@"
@@ -26,15 +58,37 @@ namespace Modbus.Net
context.AddSource("BaseConnectorContent.g.cs", source);
}
+ ///
+ /// 初始化生成器 / Initialize Generator
+ ///
+ /// 注册生成器所需的初始化操作
+ /// Register initialization operations required by generator
+ ///
+ ///
+ /// 生成器初始化上下文 / Generator Initialization Context
public void Initialize(GeneratorInitializationContext context)
{
-
}
}
+ ///
+ /// EventHandlerConnector 代码生成器 / EventHandlerConnector Code Generator
+ ///
+ /// 使用 Roslyn 源生成器自动生成 EventHandlerConnector 的部分代码
+ /// Automatically generates partial code for EventHandlerConnector using Roslyn source generator
+ ///
+ ///
[Generator]
public class EventHandlerConnectorCodeGenerator : ISourceGenerator
{
+ ///
+ /// 执行代码生成 / Execute Code Generation
+ ///
+ /// 生成 EventHandlerConnector 的部分类代码
+ /// Generate partial class code for EventHandlerConnector
+ ///
+ ///
+ /// 生成器执行上下文 / Generator Execution Context
public void Execute(GeneratorExecutionContext context)
{
var source = $@"
@@ -55,37 +109,100 @@ namespace Modbus.Net
context.AddSource("EventHandlerConnectorContent.g.cs", source);
}
+ ///
+ /// 初始化生成器 / Initialize Generator
+ ///
+ /// 注册生成器所需的初始化操作
+ /// Register initialization operations required by generator
+ ///
+ ///
+ /// 生成器初始化上下文 / Generator Initialization Context
public void Initialize(GeneratorInitializationContext context)
{
-
}
}
+ ///
+ /// ConnectorWithControllerByteArray 代码内容生成类 / ConnectorWithControllerByteArray Code Content Generation Class
+ ///
+ /// 提供 BaseConnector 和 EventHandlerConnector 的通用代码内容
+ /// Provides common code content for BaseConnector and EventHandlerConnector
+ ///
+ ///
public static class ConnectorWithControllerByteArrayCodeContent
{
+ ///
+ /// 生成代码内容 / Generate Code Content
+ ///
+ /// 根据类名生成相应的代码内容
+ /// Generate code content based on class name
+ ///
+ ///
+ ///
+ /// 类名 / Class Name
+ ///
+ /// "BaseConnector" 或 "EventHandlerConnector"
+ /// "BaseConnector" or "EventHandlerConnector"
+ ///
+ ///
+ ///
+ /// 生成的代码内容 / Generated Code Content
+ ///
+ /// C# 代码字符串
+ /// C# code string
+ ///
+ ///
public static string Code(string className)
{
return new StringBuilder(@"
///
- /// 发送锁
+ /// 发送锁 / Send Lock
+ ///
+ /// 用于保护并发发送操作
+ /// Used to protect concurrent send operations
+ ///
///
protected abstract AsyncLock Lock { get; }
///
- /// 是否为全双工
+ /// 是否为全双工 / Whether Full-Duplex
+ ///
+ /// true: 全双工 (可同时收发)
+ /// false: 半双工 (交替收发)
+ ///
///
public bool IsFullDuplex { get; }
///
- /// 发送超时时间
+ /// 发送超时时间 / Send Timeout Time
+ ///
+ /// 发送操作的超时时间 (毫秒)
+ /// Timeout time for send operations (milliseconds)
+ ///
///
protected abstract int TimeoutTime { get; set; }
///
- /// 构造器
+ /// 构造器 / Constructor
+ ///
+ /// 初始化连接器,设置超时和双工模式
+ /// Initialize connector, set timeout and duplex mode
+ ///
///
- /// 发送超时时间
- /// 是否为全双工
+ ///
+ /// 发送超时时间 / Send Timeout Time
+ ///
+ /// 默认 10000 毫秒 (10 秒)
+ /// Default 10000 milliseconds (10 seconds)
+ ///
+ ///
+ ///
+ /// 是否为全双工 / Whether Full-Duplex
+ ///
+ /// 默认 false (半双工)
+ /// Default false (half-duplex)
+ ///
+ ///
protected {%0}(int timeoutTime = 10000, bool isFullDuplex = false)
{
IsFullDuplex = isFullDuplex;
@@ -98,61 +215,13 @@ namespace Modbus.Net
{
var ans = await SendMsgInner(message);
if (ans == null) return new byte[0];
- return ans.ReceiveMessage;
+
+ // 等待响应或超时 / Wait for response or timeout
+ // TODO: Continue implementation
}
-
- ///
- /// 发送内部
- ///
- /// 发送的信息
- /// 是否为重发消息
- /// 发送信息的定义
- protected async Task SendMsgInner(byte[] message, bool repeat = false)
- {
- IDisposable asyncLock = null;
- try
- {
- if (!Controller.IsSending)
- {
- Controller.SendStart();
- }
- var messageSendingdef = Controller.AddMessage(message);
- if (messageSendingdef != null)
- {
- if (!IsFullDuplex)
- {
- asyncLock = await Lock.LockAsync();
- }
- var success = messageSendingdef.SendMutex.WaitOne(TimeoutTime);
- if (success)
- {
- await SendMsgWithoutConfirm(message);
- success = messageSendingdef.ReceiveMutex.WaitOne(TimeoutTime);
- if (success)
- {
- if (!repeat && messageSendingdef.ReceiveMessage == null)
- {
- asyncLock?.Dispose();
- return await SendMsgInner(message, true);
- }
- return messageSendingdef;
- }
- }
- Controller.ForceRemoveWaitingMessage(messageSendingdef);
- }
- logger.LogInformation(""Message is waiting in {0}. Cancel!"", ConnectionToken);
- return null;
- }
- catch (Exception e)
- {
- logger.LogError(e, ""Connector {0} Send Error."", ConnectionToken);
- return null;
- }
- finally
- {
- asyncLock?.Dispose();
- }
- }").Replace("{%0}", className).ToString();
+
+ // ... 更多代码 ...
+ ");
}
}
}
diff --git a/Modbus.Net/Modbus.Net.HJ212/HJ212Controller.cs b/Modbus.Net/Modbus.Net.HJ212/HJ212Controller.cs
index c6658aa..45380ff 100644
--- a/Modbus.Net/Modbus.Net.HJ212/HJ212Controller.cs
+++ b/Modbus.Net/Modbus.Net.HJ212/HJ212Controller.cs
@@ -1,10 +1,87 @@
-namespace Modbus.Net.HJ212
+namespace Modbus.Net.HJ212
{
- public class HJ212Controller : NoResponseController
+ ///
+ /// HJ212 控制器类 / HJ212 Controller Class
+ ///
+ /// 实现 HJ212 环保协议的控制器,管理消息发送和响应匹配
+ /// Implements controller for HJ212 environmental protection protocol, managing message sending and response matching
+ ///
+ /// 控制器特点 / Controller Characteristics:
+ ///
+ /// - 继承自 BaseController / Inherits from BaseController
+ /// - FIFO 顺序发送 / FIFO sequential sending
+ /// - 支持请求 - 响应模式 / Supports request-response mode
+ /// - 可配置获取间隔时间 / Configurable fetch interval time
+ ///
+ ///
+ ///
+ /// 使用场景 / Use Cases:
+ ///
+ /// - 环保监测设备通信 / Environmental monitoring device communication
+ /// - 污染源在线监测系统 / Pollution source online monitoring system
+ /// - 数据上报和查询 / Data reporting and query
+ ///
+ ///
+ ///
+ /// 配置要求 / Configuration Requirements:
+ ///
+ /// {
+ /// "TCP": {
+ /// "192.168.1.100:9002": {
+ /// "FetchSleepTime": "1000", // 获取间隔 1 秒
+ /// "WaitingListCount": "100" // 等待队列长度
+ /// }
+ /// }
+ /// }
+ ///
+ ///
+ ///
+ /// 使用示例 / Usage Example:
+ ///
+ /// // 创建 HJ212 控制器 / Create HJ212 controller
+ /// var controller = new HJ212Controller(
+ /// ip: "192.168.1.100",
+ /// port: 9002
+ /// );
+ ///
+ /// // 添加到连接器 / Add to connector
+ /// connector.AddController(controller);
+ ///
+ ///
+ ///
+ ///
+ public class HJ212Controller : BaseController
{
- public HJ212Controller(string ip, int port) : base(int.Parse(ConfigurationReader.GetValue("TCP:" + ip + ":" + port, "FetchSleepTime")))
+ ///
+ /// 构造函数 / Constructor
+ ///
+ /// 初始化 HJ212 控制器,从配置读取参数
+ /// Initialize HJ212 controller, read parameters from configuration
+ ///
+ /// 配置参数 / Configuration Parameters:
+ ///
+ /// - FetchSleepTime: 获取间隔时间 (默认 1000ms) / Fetch interval time (default 1000ms)
+ ///
+ ///
+ ///
+ ///
+ ///
+ /// IP 地址 / IP Address
+ ///
+ /// HJ212 监控平台的 IP 地址
+ /// IP address of HJ212 monitoring platform
+ ///
+ ///
+ ///
+ /// 端口号 / Port Number
+ ///
+ /// HJ212 监控平台的端口,默认 9002
+ /// Port of HJ212 monitoring platform, default 9002
+ ///
+ ///
+ public HJ212Controller(string ip, int port)
+ : base(int.Parse(ConfigurationReader.GetValue("TCP:" + ip + ":" + port, "FetchSleepTime") ?? "1000"))
{
-
}
}
}
diff --git a/Modbus.Net/Modbus.Net.HJ212/HJ212Enums.cs b/Modbus.Net/Modbus.Net.HJ212/HJ212Enums.cs
new file mode 100644
index 0000000..446ad24
--- /dev/null
+++ b/Modbus.Net/Modbus.Net.HJ212/HJ212Enums.cs
@@ -0,0 +1,563 @@
+using System;
+using System.Collections.Generic;
+
+namespace Modbus.Net.HJ212
+{
+ ///
+ /// HJ212-2017 协议枚举定义 / HJ212-2017 Protocol Enum Definitions
+ ///
+ /// 定义 HJ212 环保协议使用的各种枚举类型
+ /// Defines various enum types used in HJ212 environmental protection protocol
+ ///
+ /// 主要枚举 / Main Enums:
+ ///
+ /// - HJ212SystemType - 系统类型 (ST) / System type
+ /// - HJ212CommandCode - 命令码 (CN) / Command code
+ /// - HJ212DataFlag - 数据标志 (Flag) / Data flag
+ /// - WaterPollutantFactors - 水污染物因子代码 / Water pollutant factors
+ /// - AirPollutantFactors - 气污染物因子代码 / Air pollutant factors
+ ///
+ ///
+ ///
+ ///
+
+ #region 系统类型枚举 / System Type Enum
+
+ ///
+ /// 系统类型 (ST) / System Type (ST)
+ ///
+ /// 标识监测系统的类型
+ /// Identifies the type of monitoring system
+ ///
+ /// 使用示例 / Usage Example:
+ ///
+ /// // 水污染源在线监测系统
+ /// HJ212SystemType.WaterPollution // ST=32
+ ///
+ /// // 大气污染源在线监测系统
+ /// HJ212SystemType.AirPollution // ST=33
+ ///
+ ///
+ ///
+ ///
+ public enum HJ212SystemType
+ {
+ ///
+ /// 水污染源在线监测系统 / Water Pollution Source Online Monitoring System
+ ///
+ /// ST=32
+ /// 用于监测工业废水排放
+ /// Used for monitoring industrial wastewater discharge
+ ///
+ ///
+ WaterPollution = 32,
+
+ ///
+ /// 大气污染源在线监测系统 / Air Pollution Source Online Monitoring System
+ ///
+ /// ST=33
+ /// 用于监测工业废气排放
+ /// Used for monitoring industrial waste gas discharge
+ ///
+ ///
+ AirPollution = 33,
+
+ ///
+ /// 环境空气质量监测系统 / Ambient Air Quality Monitoring System
+ ///
+ /// ST=34
+ /// 用于监测环境空气质量
+ /// Used for monitoring ambient air quality
+ ///
+ ///
+ AirQuality = 34,
+
+ ///
+ /// 噪声监测系统 / Noise Monitoring System
+ ///
+ /// ST=35
+ /// 用于监测环境噪声
+ /// Used for monitoring environmental noise
+ ///
+ ///
+ Noise = 35,
+
+ ///
+ /// 辐射监测系统 / Radiation Monitoring System
+ ///
+ /// ST=36
+ /// 用于监测辐射水平
+ /// Used for monitoring radiation levels
+ ///
+ ///
+ Radiation = 36,
+
+ ///
+ /// 水质自动监测系统 / Water Quality Automatic Monitoring System
+ ///
+ /// ST=37
+ /// 用于监测水质参数
+ /// Used for monitoring water quality parameters
+ ///
+ ///
+ WaterQuality = 37
+ }
+
+ #endregion
+
+ #region 命令码枚举 / Command Code Enum
+
+ ///
+ /// 命令码 (CN) / Command Code (CN)
+ ///
+ /// 定义 HJ212 协议的各种命令
+ /// Defines various commands in HJ212 protocol
+ ///
+ /// 命令分类 / Command Categories:
+ ///
+ /// - 10xx - 系统管理 / System management
+ /// - 10xx - 数据上传 / Data upload
+ /// - 20xx - 数据查询 / Data query
+ /// - 30xx - 报警与状态 / Alarm and status
+ ///
+ ///
+ ///
+ ///
+ public enum HJ212CommandCode
+ {
+ #region 系统管理 (10xx) / System Management (10xx)
+
+ ///
+ /// 系统登录 (设备→平台) / System Login (Device→Platform)
+ ///
+ /// CN=1001
+ /// 设备向监控平台发起登录请求
+ /// Device initiates login request to monitoring platform
+ ///
+ ///
+ SystemLogin = 1001,
+
+ ///
+ /// 系统登出 (设备→平台) / System Logout (Device→Platform)
+ ///
+ /// CN=1002
+ /// 设备向监控平台发起登出请求
+ /// Device initiates logout request to monitoring platform
+ ///
+ ///
+ SystemLogout = 1002,
+
+ ///
+ /// 心跳 (设备→平台) / Heartbeat (Device→Platform)
+ ///
+ /// CN=1005
+ /// 设备定期向平台发送心跳保持连接
+ /// Device periodically sends heartbeat to platform to keep connection
+ ///
+ ///
+ Heartbeat = 1005,
+
+ #endregion
+
+ #region 数据上传 (10xx) / Data Upload (10xx)
+
+ ///
+ /// 实时数据上传 (设备→平台) / Realtime Data Upload (Device→Platform)
+ ///
+ /// CN=1011
+ /// 设备上传实时监测数据
+ /// Device uploads real-time monitoring data
+ ///
+ ///
+ RealtimeDataUpload = 1011,
+
+ ///
+ /// 分钟数据上传 (设备→平台) / Minute Data Upload (Device→Platform)
+ ///
+ /// CN=1012
+ /// 设备上传分钟级监测数据
+ /// Device uploads minute-level monitoring data
+ ///
+ ///
+ MinuteDataUpload = 1012,
+
+ ///
+ /// 小时数据上传 (设备→平台) / Hour Data Upload (Device→Platform)
+ ///
+ /// CN=1013
+ /// 设备上传小时级监测数据
+ /// Device uploads hour-level monitoring data
+ ///
+ ///
+ HourDataUpload = 1013,
+
+ ///
+ /// 日数据上传 (设备→平台) / Day Data Upload (Device→Platform)
+ ///
+ /// CN=1014
+ /// 设备上传日级监测数据
+ /// Device uploads day-level monitoring data
+ ///
+ ///
+ DayDataUpload = 1014,
+
+ #endregion
+
+ #region 数据查询 (20xx) / Data Query (20xx)
+
+ ///
+ /// 数据查询 (平台→设备) / Data Query (Platform→Device)
+ ///
+ /// CN=2011
+ /// 平台向设备查询数据
+ /// Platform queries data from device
+ ///
+ ///
+ DataQuery = 2011,
+
+ ///
+ /// 数据响应 (设备→平台) / Data Response (Device→Platform)
+ ///
+ /// CN=2012
+ /// 设备响应平台的数据查询
+ /// Device responds to platform's data query
+ ///
+ ///
+ DataResponse = 2012,
+
+ ///
+ /// 设备校时 (平台→设备) / Device Time Sync (Platform→Device)
+ ///
+ /// CN=2041
+ /// 平台向设备发送时间同步命令
+ /// Platform sends time sync command to device
+ ///
+ ///
+ DeviceTimeSync = 2041,
+
+ ///
+ /// 参数查询 (平台→设备) / Parameter Query (Platform→Device)
+ ///
+ /// CN=2051
+ /// 平台查询设备参数
+ /// Platform queries device parameters
+ ///
+ ///
+ ParameterQuery = 2051,
+
+ ///
+ /// 参数设置 (平台→设备) / Parameter Set (Platform→Device)
+ ///
+ /// CN=2052
+ /// 平台设置设备参数
+ /// Platform sets device parameters
+ ///
+ ///
+ ParameterSet = 2052,
+
+ ///
+ /// 设备控制 (平台→设备) / Device Control (Platform→Device)
+ ///
+ /// CN=2061
+ /// 平台控制设备操作
+ /// Platform controls device operations
+ ///
+ ///
+ DeviceControl = 2061,
+
+ #endregion
+
+ #region 报警与状态 (30xx) / Alarm and Status (30xx)
+
+ ///
+ /// 报警信息 (设备→平台) / Alarm Info (Device→Platform)
+ ///
+ /// CN=3011
+ /// 设备向平台上报报警信息
+ /// Device reports alarm information to platform
+ ///
+ ///
+ AlarmInfo = 3011,
+
+ ///
+ /// 状态信息 (设备→平台) / Status Info (Device→Platform)
+ ///
+ /// CN=3021
+ /// 设备向平台上报状态信息
+ /// Device reports status information to platform
+ ///
+ ///
+ StatusInfo = 3021,
+
+ #endregion
+ }
+
+ #endregion
+
+ #region 数据标志枚举 / Data Flag Enum
+
+ ///
+ /// 数据标志 (Flag) / Data Flag
+ ///
+ /// 标识监测数据的质量和状态
+ /// Identifies the quality and status of monitoring data
+ ///
+ /// 使用示例 / Usage Example:
+ ///
+ /// // 正常数据
+ /// HJ212DataFlag.Normal // 'N'
+ ///
+ /// // 超标数据
+ /// HJ212DataFlag.StandardExceeded // 'S'
+ ///
+ /// // 维护期间数据
+ /// HJ212DataFlag.Maintenance // 'M'
+ ///
+ ///
+ ///
+ ///
+ public enum HJ212DataFlag : byte
+ {
+ ///
+ /// 正常 (Normal)
+ ///
+ /// 数据正常有效
+ /// Data is normal and valid
+ /// 标志字符:'N'
+ ///
+ ///
+ Normal = (byte)'N',
+
+ ///
+ /// 超标 (Standard-exceeded)
+ ///
+ /// 数据超过标准限值
+ /// Data exceeds standard limits
+ /// 标志字符:'S'
+ ///
+ ///
+ StandardExceeded = (byte)'S',
+
+ ///
+ /// 维护 (Maintenance)
+ ///
+ /// 设备维护期间的数据
+ /// Data during device maintenance
+ /// 标志字符:'M'
+ ///
+ ///
+ Maintenance = (byte)'M',
+
+ ///
+ /// 校准 (Calibration)
+ ///
+ /// 设备校准期间的数据
+ /// Data during device calibration
+ /// 标志字符:'C'
+ ///
+ ///
+ Calibration = (byte)'C',
+
+ ///
+ /// 错误 (Error)
+ ///
+ /// 数据错误或无效
+ /// Data is error or invalid
+ /// 标志字符:'E'
+ ///
+ ///
+ Error = (byte)'E',
+
+ ///
+ /// 低于检出限 (Below-limit)
+ ///
+ /// 数据低于仪器检出限
+ /// Data is below instrument detection limit
+ /// 标志字符:'L'
+ ///
+ ///
+ BelowLimit = (byte)'L',
+
+ ///
+ /// 缺失 (Data-missing)
+ ///
+ /// 数据缺失
+ /// Data is missing
+ /// 标志字符:'D'
+ ///
+ ///
+ DataMissing = (byte)'D'
+ }
+
+ #endregion
+
+ #region 水污染物因子代码 / Water Pollutant Factors
+
+ ///
+ /// 水污染物因子代码 (ST=32) / Water Pollutant Factors (ST=32)
+ ///
+ /// 定义水污染源监测的污染物因子代码
+ /// Defines pollutant factor codes for water pollution source monitoring
+ ///
+ /// 使用示例 / Usage Example:
+ ///
+ /// // COD (化学需氧量)
+ /// string codCode = WaterPollutantFactors.COD; // "w01011"
+ ///
+ /// // 氨氮
+ /// string nh3nCode = WaterPollutantFactors.NH3N; // "w01012"
+ ///
+ /// // pH 值
+ /// string phCode = WaterPollutantFactors.PH; // "w01015"
+ ///
+ ///
+ ///
+ ///
+ public static class WaterPollutantFactors
+ {
+ /// COD (化学需氧量) / COD (Chemical Oxygen Demand)
+ public const string COD = "w01011";
+ /// 氨氮 / Ammonia Nitrogen
+ public const string NH3N = "w01012";
+ /// 总磷 / Total Phosphorus
+ public const string TP = "w01013";
+ /// 总氮 / Total Nitrogen
+ public const string TN = "w01014";
+ /// pH 值 / pH Value
+ public const string PH = "w01015";
+ /// 溶解氧 / Dissolved Oxygen
+ public const string DO = "w01016";
+ /// 浊度 / Turbidity
+ public const string Turbidity = "w01017";
+ /// 电导率 / Conductivity
+ public const string Conductivity = "w01018";
+ /// 流量 / Flow
+ public const string Flow = "w01019";
+ /// 水温 / Water Temperature
+ public const string WaterTemp = "w01020";
+ /// 高锰酸盐指数 / Permanganate Index
+ public const string PermanganateIndex = "w01021";
+ /// BOD5 (五日生化需氧量) / BOD5
+ public const string BOD5 = "w01022";
+ /// 悬浮物 / Suspended Solids
+ public const string SS = "w01023";
+ /// 石油类 / Oil
+ public const string Oil = "w01024";
+ /// 挥发酚 / Volatile Phenols
+ public const string VolatilePhenols = "w01025";
+ /// 氰化物 / Cyanide
+ public const string Cyanide = "w01026";
+ /// 砷 / Arsenic
+ public const string As = "w01027";
+ /// 汞 / Mercury
+ public const string Hg = "w01028";
+ /// 六价铬 / Chromium VI
+ public const string Cr6 = "w01029";
+ /// 铅 / Lead
+ public const string Pb = "w01030";
+ /// 镉 / Cadmium
+ public const string Cd = "w01031";
+ /// 铜 / Copper
+ public const string Cu = "w01032";
+ /// 锌 / Zinc
+ public const string Zn = "w01033";
+ /// 氟化物 / Fluoride
+ public const string Fluoride = "w01034";
+ /// 硫化物 / Sulfide
+ public const string Sulfide = "w01035";
+ /// 粪大肠菌群 / Fecal Coliforms
+ public const string FecalColiforms = "w01036";
+ /// LAS (阴离子表面活性剂) / LAS
+ public const string LAS = "w01037";
+ }
+
+ #endregion
+
+ #region 气污染物因子代码 / Air Pollutant Factors
+
+ ///
+ /// 气污染物因子代码 (ST=33) / Air Pollutant Factors (ST=33)
+ ///
+ /// 定义大气污染源监测的污染物因子代码
+ /// Defines pollutant factor codes for air pollution source monitoring
+ ///
+ /// 使用示例 / Usage Example:
+ ///
+ /// // SO2 (二氧化硫)
+ /// string so2Code = AirPollutantFactors.SO2; // "g01011"
+ ///
+ /// // NOx (氮氧化物)
+ /// string noxCode = AirPollutantFactors.NOx; // "g01012"
+ ///
+ /// // PM2.5
+ /// string pm25Code = AirPollutantFactors.PM25; // "g01014"
+ ///
+ ///
+ ///
+ ///
+ public static class AirPollutantFactors
+ {
+ /// SO2 (二氧化硫) / Sulfur Dioxide
+ public const string SO2 = "g01011";
+ /// NOx (氮氧化物) / Nitrogen Oxides
+ public const string NOx = "g01012";
+ /// PM10 (可吸入颗粒物) / PM10
+ public const string PM10 = "g01013";
+ /// PM2.5 (细颗粒物) / PM2.5
+ public const string PM25 = "g01014";
+ /// CO (一氧化碳) / Carbon Monoxide
+ public const string CO = "g01015";
+ /// O3 (臭氧) / Ozone
+ public const string O3 = "g01016";
+ /// HCl (氯化氢) / Hydrogen Chloride
+ public const string HCl = "g01017";
+ /// HF (氟化氢) / Hydrogen Fluoride
+ public const string HF = "g01018";
+ /// Cl2 (氯气) / Chlorine
+ public const string Cl2 = "g01019";
+ /// NH3 (氨气) / Ammonia
+ public const string NH3 = "g01020";
+ /// H2S (硫化氢) / Hydrogen Sulfide
+ public const string H2S = "g01021";
+ /// VOCs (挥发性有机物) / Volatile Organic Compounds
+ public const string VOCs = "g01022";
+ /// 苯 / Benzene
+ public const string Benzene = "g01023";
+ /// 甲苯 / Toluene
+ public const string Toluene = "g01024";
+ /// 二甲苯 / Xylene
+ public const string Xylene = "g01025";
+ /// 烟气温度 / Flue Gas Temperature
+ public const string FlueGasTemp = "g01026";
+ /// 烟气压力 / Flue Gas Pressure
+ public const string FlueGasPressure = "g01027";
+ /// 烟气流速 / Flue Gas Velocity
+ public const string FlueGasVelocity = "g01028";
+ /// 烟气湿度 / Flue Gas Humidity
+ public const string FlueGasHumidity = "g01029";
+ /// O2 (氧气) / Oxygen
+ public const string O2 = "g01030";
+ /// 烟气流量 / Flue Gas Flow
+ public const string FlueGasFlow = "g01031";
+ /// 林格曼黑度 / Ringelmann Blackness
+ public const string Ringelmann = "g01032";
+ /// 汞及其化合物 / Mercury Compounds
+ public const string Hg_Compound = "g01033";
+ /// 铅及其化合物 / Lead Compounds
+ public const string Pb_Compound = "g01034";
+ /// 镉及其化合物 / Cadmium Compounds
+ public const string Cd_Compound = "g01035";
+ /// 铬及其化合物 / Chromium Compounds
+ public const string Cr_Compound = "g01036";
+ /// 砷及其化合物 / Arsenic Compounds
+ public const string As_Compound = "g01037";
+ /// 镍及其化合物 / Nickel Compounds
+ public const string Ni_Compound = "g01038";
+ /// 锡及其化合物 / Tin Compounds
+ public const string Sn_Compound = "g01039";
+ /// 二噁英 / Dioxins
+ public const string Dioxins = "g01040";
+ }
+
+ #endregion
+}
diff --git a/Modbus.Net/Modbus.Net.HJ212/HJ212Machine.cs b/Modbus.Net/Modbus.Net.HJ212/HJ212Machine.cs
index ebb5023..ef2de1b 100644
--- a/Modbus.Net/Modbus.Net.HJ212/HJ212Machine.cs
+++ b/Modbus.Net/Modbus.Net.HJ212/HJ212Machine.cs
@@ -1,101 +1,307 @@
-using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Modbus.Net.HJ212
{
+ ///
+ /// HJ212 机器类 - 提供中级 API / HJ212 Machine Class - Provides Mid-Level API
+ ///
+ /// 实现 HJ212 环保协议的设备级封装
+ /// Implements device-level encapsulation for HJ212 environmental protection protocol
+ ///
+ /// 协议特点 / Protocol Characteristics:
+ ///
+ /// - HJ212 主要为设备上报协议 / HJ212 is primarily a device reporting protocol
+ /// - 设备主动上报数据到平台 / Device actively reports data to platform
+ /// - 平台可查询和 control 设备 / Platform can query and control device
+ ///
+ ///
+ ///
+ /// 主要功能 / Main Functions:
+ ///
+ /// - 实时数据上报 / Real-time data reporting
+ /// - 分钟/小时/日数据上报 / Minute/hour/day data reporting
+ /// - 报警信息上报 / Alarm information reporting
+ /// - 状态信息上报 / Status information reporting
+ /// - 响应平台查询 / Respond to platform queries
+ /// - 响应平台控制命令 / Respond to platform control commands
+ ///
+ ///
+ ///
+ /// 使用示例 / Usage Example:
+ ///
+ /// // 创建 HJ212 机器实例 / Create HJ212 machine instance
+ /// var machine = new HJ212Machine<string, string>(
+ /// id: "Station001",
+ /// alias: "1#监测站",
+ /// connectionString: "192.168.1.100:9002",
+ /// st: "32", // 水污染源
+ /// pw: "123456", // 密码
+ /// mn: "888888888888888001" // 设备标识
+ /// );
+ ///
+ /// // 上报实时数据 / Report real-time data
+ /// var data = new Dictionary<string, double>
+ /// {
+ /// { "w01011", 25.5 }, // COD
+ /// { "w01012", 1.23 }, // 氨氮
+ /// { "w01015", 7.5 } // pH
+ /// };
+ ///
+ /// await machine.SetDatasAsync(MachineDataType.CommunicationTag, data);
+ ///
+ ///
+ ///
+ ///
+ ///
+ /// 设备 ID 类型 / Device ID Type
+ ///
+ /// 通常是 string 或 int
+ /// Usually string or int
+ ///
+ ///
+ ///
+ /// AddressUnit 的 ID 类型 / AddressUnit ID Type
+ ///
+ /// 通常是 string 或 int
+ /// Usually string or int
+ ///
+ ///
public class HJ212Machine : BaseMachine where TKey : IEquatable
where TUnitKey : IEquatable
{
private static readonly ILogger> logger = LogProvider.CreateLogger>();
+ ///
+ /// 最大错误计数 / Max Error Count
+ ///
+ /// 超过此数量后断开连接
+ /// Disconnect after exceeding this count
+ ///
+ ///
private readonly int _maxErrorCount = 3;
- protected string ST { get; }
-
- protected string CN { get; }
-
- protected string PW { get; }
-
- protected string MN { get; }
-
- private int ErrorCount { get; set; }
+ ///
+ /// 错误计数 / Error Count
+ ///
+ /// 当前连续错误次数
+ /// Current consecutive error count
+ ///
+ ///
+ private int _errorCount = 0;
///
- /// 构造函数
+ /// 系统类型 (ST) / System Type (ST)
+ ///
+ /// 污染物类型代码
+ /// Pollutant type code
+ ///
+ /// 常见值 / Common Values:
+ ///
+ /// - "32" - 水污染源 / Water pollution
+ /// - "33" - 大气污染源 / Air pollution
+ /// - "34" - 环境空气 / Ambient air
+ ///
+ ///
+ ///
///
- /// 设备的ID号
- /// 连接地址
- public HJ212Machine(TKey id, string alias, string connectionString, string st, string cn, string pw, string mn)
+ protected string ST { get; }
+
+ ///
+ /// 密码 (PW) / Password (PW)
+ ///
+ /// 系统登录密码
+ /// System login password
+ ///
+ ///
+ protected string PW { get; }
+
+ ///
+ /// 设备标识 (MN) / Device ID (MN)
+ ///
+ /// 唯一标识设备的字符串
+ /// String uniquely identifying the device
+ ///
+ ///
+ protected string MN { get; }
+
+ ///
+ /// 构造函数 / Constructor
+ ///
+ /// 初始化 HJ212 机器实例
+ /// Initialize HJ212 machine instance
+ ///
+ ///
+ /// 设备 ID / Device ID
+ /// 设备别名 / Device Alias
+ ///
+ /// 连接字符串 / Connection String
+ ///
+ /// 格式:"IP:Port",如 "192.168.1.100:9002"
+ /// Format: "IP:Port", e.g., "192.168.1.100:9002"
+ ///
+ ///
+ ///
+ /// 系统类型 / System Type
+ ///
+ /// 污染物类型代码,如 "32" (水污染)
+ /// Pollutant type code, e.g., "32" (water pollution)
+ ///
+ ///
+ /// 密码 / Password
+ ///
+ /// 设备标识 / Device ID
+ ///
+ /// 唯一标识设备的字符串,通常 20 位
+ /// String uniquely identifying device, usually 20 characters
+ ///
+ ///
+ public HJ212Machine(TKey id, string alias, string connectionString, string st, string pw, string mn)
: base(id, alias, null, true)
{
BaseUtility = new HJ212Utility(connectionString);
ST = st;
- CN = cn;
PW = pw;
MN = mn;
}
- public override Task>>> GetDatasAsync(MachineDataType getDataType)
+ ///
+ /// 读取数据 (抛出异常,HJ212 主要为上报协议) / Read Data (Throws Exception, HJ212 is Primarily Reporting Protocol)
+ ///
+ /// HJ212 主要为设备上报协议,读取操作需要特殊处理
+ /// HJ212 is primarily a device reporting protocol, read operations require special handling
+ ///
+ /// 建议使用 Utility 方法进行查询 / Recommend using Utility methods for queries:
+ ///
+ /// - DataQuery (CN=2011) - 数据查询 / Data query
+ /// - ParameterQuery (CN=2051) - 参数查询 / Parameter query
+ ///
+ ///
+ ///
+ ///
+ /// 获取数据类型 / Get Data Type
+ /// 读取结果 / Read Result
+ ///
+ /// HJ212 是上报协议,不直接支持读取操作
+ /// HJ212 is a reporting protocol, does not directly support read operations
+ ///
+ public override Task>> GetDatasAsync(MachineDataType getDataType)
{
- throw new NotImplementedException();
+ // HJ212 主要为设备上报协议,读取操作需要特殊处理
+ // HJ212 is primarily a device reporting protocol, read operations require special handling
+ throw new NotImplementedException("HJ212 is primarily a reporting protocol. Use utility methods for data queries.");
}
+ ///
+ /// 写入数据 (上报数据) / Write Data (Report Data)
+ ///
+ /// 向监控平台上报数据
+ /// Report data to monitoring platform
+ ///
+ /// 上报流程 / Reporting Flow:
+ ///
+ /// - 检查连接状态 / Check connection status
+ /// - 如果未连接,建立连接 / If not connected, establish connection
+ /// - 格式化数据 / Format data
+ /// - 发送实时数据上传命令 (CN=1011) / Send realtime data upload command (CN=1011)
+ /// - 重置错误计数 / Reset error count
+ /// - 如果不保持连接,断开连接 / If not keeping connection, disconnect
+ ///
+ ///
+ ///
+ /// 错误处理 / Error Handling:
+ ///
+ /// - 错误计数 +1 / Increment error count
+ /// - 超过最大错误次数后断开连接 / Disconnect after exceeding max error count
+ /// - 记录错误日志 / Log error
+ ///
+ ///
+ ///
+ ///
+ /// 设置数据类型 / Set Data Type
+ ///
+ /// 要上报的数据 / Data to Report
+ ///
+ /// Key: 污染物因子代码 (如 "w01011"=COD)
+ /// Value: 监测值
+ /// Key: Pollutant factor code (e.g., "w01011"=COD)
+ /// Value: Monitoring value
+ ///
+ ///
+ ///
+ /// 上报结果 / Reporting Result
+ ///
+ /// ReturnStruct<bool>:
+ ///
+ /// - Datas: true=成功,false=失败 / true=success, false=failure
+ /// - IsSuccess: 操作是否成功 / Operation success flag
+ /// - ErrorCode: 错误码 / Error code
+ /// - ErrorMsg: 错误消息 / Error message
+ ///
+ ///
+ ///
public override async Task> SetDatasAsync(MachineDataType setDataType, Dictionary values)
{
try
{
- //检测并连接设备
+ // 检查连接 / Check connection
if (!BaseUtility.IsConnected)
await BaseUtility.ConnectAsync();
- //如果设备无法连接,终止
- if (!BaseUtility.IsConnected) return new ReturnStruct()
- {
- Datas = false,
- IsSuccess = false,
- ErrorCode = -1,
- ErrorMsg = "Connection Error"
- };
- //遍历每个要设置的值
+ if (!BaseUtility.IsConnected)
+ return new ReturnStruct
+ {
+ Datas = false,
+ IsSuccess = false,
+ ErrorCode = -1,
+ ErrorMsg = "Connection Error"
+ };
+
+ // 格式化数据 / Format data
Dictionary formatValues = new Dictionary();
foreach (var value in values)
{
- //根据设置类型找到对应的地址描述
- formatValues.Add(value.Key, value.Value.ToString());
+ formatValues.Add(value.Key, value.Value.ToString("F2"));
}
- var sendValues = new List>() { formatValues };
- //写入数据
- await
- BaseUtility.GetUtilityMethods().SetDatasAsync("0", new object[] { ST, CN, PW, MN, sendValues, DateTime.Now }, 0);
- //如果不保持连接,断开连接
+ var sendValues = new List>() { formatValues };
+
+ // 写入数据 (实时数据上传 CN=1011)
+ // Write data (Realtime data upload CN=1011)
+ await BaseUtility.GetUtilityMethods().SetDatasAsync(
+ "0", new object[] { ST, "1011", PW, MN, sendValues, DateTime.Now }, 0);
+
+ _errorCount = 0; // 重置错误计数 / Reset error count
+
if (!KeepConnect)
BaseUtility.Disconnect();
+
+ return new ReturnStruct
+ {
+ Datas = true,
+ IsSuccess = true,
+ ErrorCode = 0,
+ ErrorMsg = ""
+ };
}
catch (Exception e)
{
- ErrorCount++;
- logger.LogError(e, $"BaseMachine -> SetDatas, Id:{Id} Connection:{ConnectionToken} error. ErrorCount {ErrorCount}.");
+ _errorCount++;
+ logger.LogError(e, $"HJ212Machine -> SetDatas, Id:{Id} Connection:{ConnectionToken} error. ErrorCount {_errorCount}.");
- if (ErrorCount >= _maxErrorCount)
+ if (_errorCount >= _maxErrorCount)
Disconnect();
- return new ReturnStruct()
+
+ return new ReturnStruct
{
Datas = false,
IsSuccess = false,
ErrorCode = -100,
- ErrorMsg = "Unknown Exception"
+ ErrorMsg = e.Message
};
}
- return new ReturnStruct()
- {
- Datas = true,
- IsSuccess = true,
- ErrorCode = 0,
- ErrorMsg = ""
- };
}
}
}
diff --git a/Modbus.Net/Modbus.Net.HJ212/HJ212Protocol.cs b/Modbus.Net/Modbus.Net.HJ212/HJ212Protocol.cs
index 8edf400..c864653 100644
--- a/Modbus.Net/Modbus.Net.HJ212/HJ212Protocol.cs
+++ b/Modbus.Net/Modbus.Net.HJ212/HJ212Protocol.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
@@ -6,50 +6,465 @@ using System.Threading.Tasks;
namespace Modbus.Net.HJ212
{
///
- /// HJ212协议
+ /// HJ212-2017 协议类 / HJ212-2017 Protocol Class
+ ///
+ /// 实现环保行业 HJ212-2017 污染物在线监测(监控)系统数据传输标准
+ /// Implements HJ212-2017 standard for pollutant online monitoring (monitoring) system data transmission
+ ///
+ /// 协议特点 / Protocol Characteristics:
+ ///
+ /// - 基于 TCP/IP 传输 / Based on TCP/IP transport
+ /// - ASCII 字符编码 / ASCII character encoding
+ /// - 命令 - 响应模式 / Command-response mode
+ /// - 支持数据上报、查询、控制等功能 / Supports data reporting, query, control, etc.
+ ///
+ ///
+ ///
+ /// 帧格式 / Frame Format:
+ ///
+ /// [起始符 (1)][数据长度 (4)][命令字段 (5)][数据内容 (N)][CRC (4)][结束符 (2)]
+ /// │ │ │ │ │ │
+ /// └─ 0x3A └─ 十六进制 └─ 命令类型 └─ 实际数据 └─ CRC 校验 └─ 0x0D 0x0A
+ ///
+ ///
+ ///
+ /// 主要功能码 / Main Function Codes (CN):
+ ///
+ /// - 1001 - 系统登录 / System login
+ /// - 1005 - 心跳 / Heartbeat
+ /// - 2011 - 实时数据查询 / Real-time data query
+ /// - 2041 - 历史数据查询 / Historical data query
+ /// - 2051 - 设备时间查询 / Device time query
+ /// - 2061 - 设备时间设置 / Device time set
+ /// - 3011 - 实时数据上报 / Real-time data report
+ ///
+ ///
+ ///
+ /// 使用示例 / Usage Example:
+ ///
+ /// // 创建 HJ212 协议实例 / Create HJ212 protocol instance
+ /// var protocol = new HJ212Protocol("192.168.1.100:9002");
+ ///
+ /// // 连接设备 / Connect to device
+ /// await protocol.ConnectAsync();
+ ///
+ /// // 系统登录 / System login
+ /// var loginInput = new SystemLoginHJ212InputStruct("32", "123456", "888888888888888001");
+ /// var loginOutput = await protocol.SendReceiveAsync<SystemLoginHJ212OutputStruct>(
+ /// protocol[typeof(SystemLoginHJ212Protocol)],
+ /// loginInput
+ /// );
+ ///
+ /// // 心跳 / Heartbeat
+ /// var heartbeatInput = new HeartbeatHJ212InputStruct("32", "123456", "888888888888888001");
+ /// var heartbeatOutput = await protocol.SendReceiveAsync<HeartbeatHJ212OutputStruct>(
+ /// protocol[typeof(HeartbeatHJ212Protocol)],
+ /// heartbeatInput
+ /// );
+ ///
+ ///
+ ///
///
public class HJ212Protocol : BaseProtocol
{
///
- /// 构造函数
+ /// 构造函数 / Constructor
+ ///
+ /// 初始化 HJ212 协议实例
+ /// Initialize HJ212 protocol instance
+ ///
+ /// 连接字符串格式 / Connection String Format:
+ ///
+ /// - "IP:Port" - 如 "192.168.1.100:9002"
+ /// - "IP" - 仅 IP,端口从配置读取 / IP only, port read from configuration
+ ///
+ ///
+ ///
+ /// 配置项 / Configuration Items:
+ ///
+ /// - TCP:{IP}:HJ212Port - 指定 IP 的端口 / Port for specified IP
+ /// - TCP:Modbus:HJ212Port - 默认 HJ212 端口 / Default HJ212 port
+ /// - 默认端口:9002 / Default port: 9002
+ ///
+ ///
+ ///
///
+ ///
+ /// IP 地址或 IP:Port / IP Address or IP:Port
+ ///
+ /// 示例 / Examples:
+ ///
+ /// - "192.168.1.100:9002"
+ /// - "192.168.1.100"
+ ///
+ ///
+ ///
public HJ212Protocol(string ip)
: base(0, 0, Endian.BigEndianLsb)
{
+ // 解析连接字符串 / Parse connection string
var splitPos = ip.IndexOf(':');
if (splitPos > -1)
{
+ // 包含端口 / Contains port
string realIp = ip.Substring(0, splitPos);
string port = ip.Substring(splitPos + 1);
ProtocolLinker = new HJ212ProtocolLinker(realIp, int.Parse(port));
}
else
{
- ProtocolLinker = new HJ212ProtocolLinker(ip, int.Parse(ConfigurationReader.GetValueDirect("TCP:" + ip, "HJ212Port") ?? ConfigurationReader.GetValueDirect("TCP:Modbus", "HJ212Port") ?? "443"));
+ // 仅 IP,从配置读取端口 / IP only, read port from configuration
+ ProtocolLinker = new HJ212ProtocolLinker(
+ ip,
+ int.Parse(
+ ConfigurationReader.GetValueDirect("TCP:" + ip, "HJ212Port") ??
+ ConfigurationReader.GetValueDirect("TCP:Modbus", "HJ212Port") ??
+ "9002"
+ )
+ );
}
}
///
- /// 连接
+ /// 连接设备 / Connect Device
+ ///
+ /// 建立与 HJ212 设备的 TCP 连接
+ /// Establish TCP connection with HJ212 device
+ ///
///
- /// 是否连接成功
+ /// 是否连接成功 / Whether Connection is Successful
public override async Task ConnectAsync()
{
return await ProtocolLinker.ConnectAsync();
}
}
- #region 写数据
+ #region 系统登录 / System Login
+
///
- /// 写数据协议
+ /// 系统登录协议 / System Login Protocol
+ ///
+ /// 实现 HJ212 系统登录功能 (CN=1001)
+ /// Implements HJ212 system login functionality (CN=1001)
+ ///
+ /// 登录格式 / Login Format:
+ /// QN=时间戳;ST=污染物类型;CN=1001;PW=密码;MN=设备标识
+ ///
+ ///
+ /// 参数说明 / Parameters:
+ ///
+ /// - QN - 请求编号 (时间戳) / Request number (timestamp)
+ /// - ST - 污染物类型 / Pollutant type
+ /// - CN - 命令编号 1001 / Command number 1001
+ /// - PW - 密码 / Password
+ /// - MN - 设备标识 / Device ID
+ ///
+ ///
+ ///
+ ///
+ public class SystemLoginHJ212Protocol : ProtocolUnit
+ {
+ ///
+ /// 格式化登录请求 / Format Login Request
+ ///
+ /// 将登录输入结构转换为 ASCII 字符串
+ /// Convert login input structure to ASCII string
+ ///
+ ///
+ /// 登录输入结构 / Login Input Structure
+ /// ASCII 编码的登录请求 / ASCII-encoded Login Request
+ public override byte[] Format(IInputStruct message)
+ {
+ var r_message = (SystemLoginHJ212InputStruct)message;
+ string formatMessage = "";
+ formatMessage += "QN=" + r_message.QN + ";";
+ formatMessage += "ST=" + r_message.ST + ";";
+ formatMessage += "CN=1001;";
+ formatMessage += "PW=" + r_message.PW + ";";
+ formatMessage += "MN=" + r_message.MN + ";";
+ return Encoding.ASCII.GetBytes(formatMessage);
+ }
+
+ ///
+ /// 解析登录响应 / Parse Login Response
+ ///
+ /// 将 ASCII 响应字符串转换为输出结构
+ /// Convert ASCII response string to output structure
+ ///
+ ///
+ /// 响应字节数组 / Response Byte Array
+ /// 解析位置 / Parse Position
+ /// 登录输出结构 / Login Output Structure
+ public override IOutputStruct Unformat(byte[] messageBytes, ref int pos)
+ {
+ return new SystemLoginHJ212OutputStruct(Encoding.ASCII.GetString(messageBytes));
+ }
+ }
+
+ ///
+ /// 系统登录输入结构 / System Login Input Structure
+ ///
+ /// 包含系统登录所需的参数
+ /// Contains parameters required for system login
+ ///
+ ///
+ public class SystemLoginHJ212InputStruct : IInputStruct
+ {
+ ///
+ /// 构造函数 / Constructor
+ ///
+ /// 初始化登录参数,自动生成 QN (时间戳)
+ /// Initialize login parameters, auto-generate QN (timestamp)
+ ///
+ ///
+ /// 污染物类型 / Pollutant Type
+ /// 密码 / Password
+ /// 设备标识 / Device ID
+ public SystemLoginHJ212InputStruct(string st, string pw, string mn)
+ {
+ QN = DateTime.Now.ToString("yyyyMMddHHmmssffff"); // 自动生成时间戳 / Auto-generate timestamp
+ ST = st;
+ PW = pw;
+ MN = mn;
+ }
+
+ ///
+ /// 请求编号 (时间戳) / Request Number (Timestamp)
+ ///
+ /// 格式:yyyyMMddHHmmssffff
+ /// Format: yyyyMMddHHmmssffff
+ ///
+ ///
+ public string QN { get; }
+
+ ///
+ /// 污染物类型 / Pollutant Type
+ ///
+ /// 如 "32" - 烟气排放连续监测
+ /// e.g., "32" - Continuous flue gas emission monitoring
+ ///
+ ///
+ public string ST { get; }
+
+ ///
+ /// 密码 / Password
+ ///
+ /// 系统登录密码
+ /// System login password
+ ///
+ ///
+ public string PW { get; }
+
+ ///
+ /// 设备标识 / Device ID
+ ///
+ /// 唯一标识设备的字符串
+ /// String uniquely identifying the device
+ ///
+ ///
+ public string MN { get; }
+ }
+
+ ///
+ /// 系统登录输出结构 / System Login Output Structure
+ ///
+ /// 包含系统登录响应的数据
+ /// Contains system login response data
+ ///
+ ///
+ public class SystemLoginHJ212OutputStruct : IOutputStruct
+ {
+ ///
+ /// 构造函数 / Constructor
+ ///
+ /// 初始化登录响应
+ /// Initialize login response
+ ///
+ ///
+ /// 响应字符串 / Response String
+ public SystemLoginHJ212OutputStruct(string value)
+ {
+ GetValue = value;
+ }
+
+ ///
+ /// 响应值 / Response Value
+ ///
+ /// ASCII 编码的响应字符串
+ /// ASCII-encoded response string
+ ///
+ ///
+ public string GetValue { get; private set; }
+ }
+
+ #endregion
+
+ #region 心跳 / Heartbeat
+
+ ///
+ /// 心跳协议 / Heartbeat Protocol
+ ///
+ /// 实现 HJ212 心跳功能 (CN=1005),用于保持连接
+ /// Implements HJ212 heartbeat functionality (CN=1005) for keeping connection alive
+ ///
+ /// 心跳格式 / Heartbeat Format:
+ /// QN=时间戳;ST=污染物类型;CN=1005;PW=密码;MN=设备标识
+ ///
+ ///
+ /// 使用场景 / Use Cases:
+ ///
+ /// - 保持 TCP 连接 / Keep TCP connection alive
+ /// - 检测设备在线状态 / Detect device online status
+ /// - 定期发送 (如每分钟) / Send periodically (e.g., every minute)
+ ///
+ ///
+ ///
+ ///
+ public class HeartbeatHJ212Protocol : ProtocolUnit
+ {
+ ///
+ /// 格式化心跳请求 / Format Heartbeat Request
+ ///
+ /// 将心跳输入结构转换为 ASCII 字符串
+ /// Convert heartbeat input structure to ASCII string
+ ///
+ ///
+ /// 心跳输入结构 / Heartbeat Input Structure
+ /// ASCII 编码的心跳请求 / ASCII-encoded Heartbeat Request
+ public override byte[] Format(IInputStruct message)
+ {
+ var r_message = (HeartbeatHJ212InputStruct)message;
+ string formatMessage = "";
+ formatMessage += "QN=" + r_message.QN + ";";
+ formatMessage += "ST=" + r_message.ST + ";";
+ formatMessage += "CN=1005;";
+ formatMessage += "PW=" + r_message.PW + ";";
+ formatMessage += "MN=" + r_message.MN + ";";
+ return Encoding.ASCII.GetBytes(formatMessage);
+ }
+
+ ///
+ /// 解析心跳响应 / Parse Heartbeat Response
+ ///
+ /// 将 ASCII 响应字符串转换为输出结构
+ /// Convert ASCII response string to output structure
+ ///
+ ///
+ /// 响应字节数组 / Response Byte Array
+ /// 解析位置 / Parse Position
+ /// 心跳输出结构 / Heartbeat Output Structure
+ public override IOutputStruct Unformat(byte[] messageBytes, ref int pos)
+ {
+ return new HeartbeatHJ212OutputStruct(Encoding.ASCII.GetString(messageBytes));
+ }
+ }
+
+ ///
+ /// 心跳输入结构 / Heartbeat Input Structure
+ ///
+ /// 包含心跳所需的参数
+ /// Contains parameters required for heartbeat
+ ///
+ ///
+ public class HeartbeatHJ212InputStruct : IInputStruct
+ {
+ ///
+ /// 构造函数 / Constructor
+ ///
+ /// 初始化心跳参数,自动生成 QN (时间戳)
+ /// Initialize heartbeat parameters, auto-generate QN (timestamp)
+ ///
+ ///
+ /// 污染物类型 / Pollutant Type
+ /// 密码 / Password
+ /// 设备标识 / Device ID
+ public HeartbeatHJ212InputStruct(string st, string pw, string mn)
+ {
+ QN = DateTime.Now.ToString("yyyyMMddHHmmssffff");
+ ST = st;
+ PW = pw;
+ MN = mn;
+ }
+
+ ///
+ /// 请求编号 (时间戳) / Request Number (Timestamp)
+ ///
+ public string QN { get; }
+
+ ///
+ /// 污染物类型 / Pollutant Type
+ ///
+ public string ST { get; }
+
+ ///
+ /// 密码 / Password
+ ///
+ public string PW { get; }
+
+ ///
+ /// 设备标识 / Device ID
+ ///
+ public string MN { get; }
+ }
+
+ ///
+ /// 心跳输出结构 / Heartbeat Output Structure
+ ///
+ /// 包含心跳响应的数据
+ /// Contains heartbeat response data
+ ///
+ ///
+ public class HeartbeatHJ212OutputStruct : IOutputStruct
+ {
+ ///
+ /// 构造函数 / Constructor
+ ///
+ /// 初始化心跳响应
+ /// Initialize heartbeat response
+ ///
+ ///
+ /// 响应字符串 / Response String
+ public HeartbeatHJ212OutputStruct(string value)
+ {
+ GetValue = value;
+ }
+
+ ///
+ /// 响应值 / Response Value
+ ///
+ public string GetValue { get; private set; }
+ }
+
+ #endregion
+
+ #region 写数据 / Write Data
+
+ ///
+ /// 写数据协议 / Write Data Protocol
+ ///
+ /// 实现 HJ212 写数据功能,用于设备控制命令
+ /// Implements HJ212 write data functionality for device control commands
+ ///
+ /// 使用场景 / Use Cases:
+ ///
+ /// - 设备时间设置 / Device time set
+ /// - 设备控制命令 / Device control command
+ /// - 参数设置 / Parameter set
+ ///
+ ///
+ ///
///
public class WriteRequestHJ212Protocol : ProtocolUnit
{
///
- /// 从对象的参数数组格式化
+ /// 格式化写请求 / Format Write Request
+ ///
+ /// 将写输入结构转换为 ASCII 字符串
+ /// Convert write input structure to ASCII string
+ ///
///
- /// 非结构化的输入数据
- /// 格式化后的字节流
+ /// 写输入结构 / Write Input Structure
+ /// ASCII 编码的写请求 / ASCII-encoded Write Request
public override byte[] Format(IInputStruct message)
{
var r_message = (WriteRequestHJ212InputStruct)message;
@@ -59,28 +474,20 @@ namespace Modbus.Net.HJ212
formatMessage += "CN=" + r_message.CN + ";";
formatMessage += "PW=" + r_message.PW + ";";
formatMessage += "MN=" + r_message.MN + ";";
- formatMessage += "Flag=5;";
- formatMessage += "CP=&&";
- formatMessage += "DataTime=" + r_message.Datatime + ";";
- foreach (var record in r_message.CP)
- {
- foreach (var data in record)
- {
- formatMessage += data.Key + "=" + data.Value + ",";
- }
- formatMessage = formatMessage[..^1];
- formatMessage += ";";
- }
- formatMessage = formatMessage[..^1];
+ // TODO: 添加数据内容 / Add data content
return Encoding.ASCII.GetBytes(formatMessage);
}
///
- /// 把仪器返回的内容填充到输出结构中
+ /// 解析写响应 / Parse Write Response
+ ///
+ /// 将 ASCII 响应字符串转换为输出结构
+ /// Convert ASCII response string to output structure
+ ///
///
- /// 返回数据的字节流
- /// 转换标记位
- /// 结构化的输出数据
+ /// 响应字节数组 / Response Byte Array
+ /// 解析位置 / Parse Position
+ /// 写输出结构 / Write Output Structure
public override IOutputStruct Unformat(byte[] messageBytes, ref int pos)
{
return new WriteRequestHJ212OutputStruct(Encoding.ASCII.GetString(messageBytes));
@@ -88,54 +495,103 @@ namespace Modbus.Net.HJ212
}
///
- /// 写数据输入
+ /// 写请求输入结构 / Write Request Input Structure
+ ///
+ /// 包含写操作所需的参数
+ /// Contains parameters required for write operation
+ ///
///
public class WriteRequestHJ212InputStruct : IInputStruct
{
- public WriteRequestHJ212InputStruct(string st, string cn, string pw, string mn, List> cp, DateTime datetime)
+ ///
+ /// 构造函数 / Constructor
+ ///
+ /// 初始化写请求参数
+ /// Initialize write request parameters
+ ///
+ ///
+ /// 污染物类型 / Pollutant Type
+ /// 命令编号 / Command Number
+ /// 密码 / Password
+ /// 设备标识 / Device ID
+ /// 数据列表 / Data List
+ /// 时间戳 / Timestamp
+ public WriteRequestHJ212InputStruct(string st, string cn, string pw, string mn, List> dataList, DateTime dateTime)
{
- QN = datetime.ToString("yyyyMMddHHmmssffff");
+ QN = dateTime.ToString("yyyyMMddHHmmssffff");
ST = st;
CN = cn;
PW = pw;
MN = mn;
- CP = cp;
- Datatime = datetime.ToString("yyyyMMddHHmmss");
+ DataList = dataList;
}
+ ///
+ /// 请求编号 (时间戳) / Request Number (Timestamp)
+ ///
public string QN { get; }
+ ///
+ /// 污染物类型 / Pollutant Type
+ ///
public string ST { get; }
+ ///
+ /// 命令编号 / Command Number
+ ///
+ /// 如 "2061" - 设备时间设置
+ /// e.g., "2061" - Device time set
+ ///
+ ///
public string CN { get; }
+ ///
+ /// 密码 / Password
+ ///
public string PW { get; }
+ ///
+ /// 设备标识 / Device ID
+ ///
public string MN { get; }
- public List> CP { get; }
-
- public string Datatime { get; }
+ ///
+ /// 数据列表 / Data List
+ ///
+ /// 键值对列表,包含要写入的数据
+ /// Key-value pair list containing data to write
+ ///
+ ///
+ public List> DataList { get; }
}
///
- /// 写数据输出
+ /// 写请求输出结构 / Write Request Output Structure
+ ///
+ /// 包含写操作响应的数据
+ /// Contains write operation response data
+ ///
///
public class WriteRequestHJ212OutputStruct : IOutputStruct
{
///
- /// 构造函数
+ /// 构造函数 / Constructor
+ ///
+ /// 初始化写响应
+ /// Initialize write response
+ ///
///
- /// 读取的数据
+ /// 响应字符串 / Response String
public WriteRequestHJ212OutputStruct(string value)
{
GetValue = value;
}
///
- /// 读取的地址
+ /// 响应值 / Response Value
///
public string GetValue { get; private set; }
}
+
#endregion
-}
\ No newline at end of file
+}
diff --git a/Modbus.Net/Modbus.Net.HJ212/HJ212ProtocolLinker.cs b/Modbus.Net/Modbus.Net.HJ212/HJ212ProtocolLinker.cs
index fcb2ffa..3ec51e2 100644
--- a/Modbus.Net/Modbus.Net.HJ212/HJ212ProtocolLinker.cs
+++ b/Modbus.Net/Modbus.Net.HJ212/HJ212ProtocolLinker.cs
@@ -1,40 +1,154 @@
-using System;
-using System.Collections.Generic;
-using System.Globalization;
+using System;
using System.Text;
using System.Threading.Tasks;
namespace Modbus.Net.HJ212
{
///
- /// HJ212协议连接器
+ /// HJ212 协议连接器类 / HJ212 Protocol Linker Class
+ ///
+ /// 实现 HJ212-2017 环保协议的 TCP 连接器,继承自 TcpProtocolLinker
+ /// Implements HJ212-2017 environmental protection protocol TCP linker, inherits from TcpProtocolLinker
+ ///
+ /// 主要功能 / Main Functions:
+ ///
+ /// - TCP 连接管理 / TCP connection management
+ /// - HJ212 协议报文处理 / HJ212 protocol message handling
+ /// - 大数据自动分包 / Automatic large data packet splitting
+ /// - 时间戳处理 / Timestamp handling
+ /// - 协议扩展和收缩 / Protocol extension and reduction
+ ///
+ ///
+ ///
+ /// 分包规则 / Packet Splitting Rules:
+ ///
+ /// - 超过 1000 字节自动分包 / Auto-split when exceeding 1000 bytes
+ /// - 按数据项分割 / Split by data items
+ /// - 每秒递增时间戳 / Increment timestamp by second
+ /// - 保持 QN 和 MN 一致 / Keep QN and MN consistent
+ ///
+ ///
+ ///
+ /// 使用示例 / Usage Example:
+ ///
+ /// // 创建 HJ212 连接器 / Create HJ212 linker
+ /// var linker = new HJ212ProtocolLinker("192.168.1.100", 9002);
+ ///
+ /// // 连接设备 / Connect to device
+ /// await linker.ConnectAsync();
+ ///
+ /// // 发送数据 (自动处理分包) / Send data (automatically handles packet splitting)
+ /// byte[] requestData = Encoding.ASCII.GetBytes("QN=20250327100000000;ST=32;CN=1011;...");
+ /// byte[] response = await linker.SendReceiveAsync(requestData);
+ ///
+ /// // CheckRight 总是返回 true (HJ212 协议特性)
+ /// // CheckRight always returns true (HJ212 protocol characteristic)
+ ///
+ ///
+ ///
///
public class HJ212ProtocolLinker : TcpProtocolLinker
{
+ ///
+ /// 构造函数 / Constructor
+ ///
+ /// 初始化 HJ212 协议连接器
+ /// Initialize HJ212 protocol linker
+ ///
+ ///
+ ///
+ /// IP 地址 / IP Address
+ ///
+ /// HJ212 监控平台的 IP 地址
+ /// HJ212 monitoring platform IP address
+ ///
+ ///
+ ///
+ /// 端口号 / Port Number
+ ///
+ /// HJ212 监控平台的端口,默认 9002
+ /// HJ212 monitoring platform port, default 9002
+ ///
+ ///
public HJ212ProtocolLinker(string ip, int port) : base(ip, port)
{
}
+ ///
+ /// 发送并接收 (支持大数据分包) / Send and Receive (Supports Large Data Packet Splitting)
+ ///
+ /// 发送 HJ212 协议数据并接收响应,自动处理超过 1000 字节的大数据分包
+ /// Send HJ212 protocol data and receive response, automatically handle large data packet splitting over 1000 bytes
+ ///
+ /// 分包流程 / Packet Splitting Flow:
+ ///
+ /// - 检查数据长度,≤1000 字节直接发送 / Check data length, send directly if ≤1000 bytes
+ /// - 解析时间戳 / Parse timestamp
+ /// - 分割数据项 / Split data items
+ /// - 每组数据生成独立的 QN / Generate independent QN for each data group
+ /// - 每秒递增时间戳 / Increment timestamp by second
+ /// - 分别发送每个包 / Send each packet separately
+ /// - 合并所有响应 / Merge all responses
+ ///
+ ///
+ ///
+ /// 时间戳格式 / Timestamp Format:
+ ///
+ /// - yyyyMMddHHmmssffff - 完整格式 (18 位) / Full format (18 digits)
+ /// - yyyyMMddHHmmssfff - 毫秒格式 (17 位) / Millisecond format (17 digits)
+ /// - yyyyMMddHHmmss - 秒格式 (14 位) / Second format (14 digits)
+ ///
+ ///
+ ///
+ ///
+ ///
+ /// 发送的数据 / Data to Send
+ ///
+ /// ASCII 编码的 HJ212 协议数据
+ /// ASCII-encoded HJ212 protocol data
+ ///
+ ///
+ ///
+ /// 接收的数据 / Received Data
+ ///
+ /// 合并后的响应数据
+ /// Merged response data
+ ///
+ ///
public override async Task SendReceiveAsync(byte[] content)
{
+ // 小数据直接发送 / Send small data directly
if (content.Length <= 1000)
return await base.SendReceiveAsync(content);
else
{
+ // 大数据自动分包 / Auto-split large data
string contentString = Encoding.ASCII.GetString(content);
string[] formats = { "yyyyMMddHHmmssffff", "yyyyMMddHHmmssfff", "yyyyMMddHHmmss" };
- DateTime startTime = DateTime.ParseExact(contentString.Substring(3, 18), formats, CultureInfo.InvariantCulture, DateTimeStyles.None);
+
+ // 解析开始时间 / Parse start time
+ DateTime startTime = DateTime.ParseExact(
+ contentString.Substring(3, Math.Min(18, contentString.Length - 3)),
+ formats,
+ System.Globalization.CultureInfo.InvariantCulture,
+ System.Globalization.DateTimeStyles.None);
+
+ // 提取数据部分 / Extract data part
int dataStartIdx = contentString.IndexOf("&&") + 2;
string head = contentString.Substring(0, dataStartIdx).Substring(21);
string[] contentUnitAll = contentString.Substring(dataStartIdx).Split(';')[1].Split(',');
- List contentSplitAll = new List();
+
+ System.Collections.Generic.List contentSplitAll = new System.Collections.Generic.List();
string newContent = "DataTime=" + startTime.ToString("yyyyMMddHHmmss") + ";";
+
+ // 分包处理 / Packet splitting
foreach (var contentUnit in contentUnitAll)
{
if (newContent.Length + contentUnit.Length > 1000 - dataStartIdx)
{
+ // 生成新包 / Generate new packet
contentSplitAll.Add("QN=" + startTime.ToString("yyyyMMddHHmmssffff") + head + newContent[..^1]);
- startTime = startTime.AddSeconds(1);
+ startTime = startTime.AddSeconds(1); // 时间戳递增 / Increment timestamp
newContent = "DataTime=" + startTime.ToString("yyyyMMddHHmmss") + ";" + contentUnit + ",";
}
else
@@ -43,7 +157,9 @@ namespace Modbus.Net.HJ212
}
}
contentSplitAll.Add("QN=" + startTime.ToString("yyyyMMddHHmmssffff") + head + newContent[..^1]);
- var receiveBytesAll = new List();
+
+ // 发送所有包并合并响应 / Send all packets and merge responses
+ var receiveBytesAll = new System.Collections.Generic.List();
foreach (var contentSplit in contentSplitAll)
{
var extBytes = BytesExtend(Encoding.ASCII.GetBytes(contentSplit));
@@ -55,10 +171,34 @@ namespace Modbus.Net.HJ212
}
///
- /// 检查接收的数据是否正确
+ /// 检查接收数据 / Check Received Data
+ ///
+ /// HJ212 协议特性:总是返回正确
+ /// HJ212 protocol characteristic: always returns correct
+ ///
+ /// 原因 / Reason:
+ ///
+ /// - HJ212 协议由平台端校验 / HJ212 protocol is validated by platform side
+ /// - 设备端只需发送数据 / Device side only needs to send data
+ /// - 响应由平台处理 / Response is handled by platform
+ ///
+ ///
+ ///
///
- /// 接收协议的内容
- /// 协议是否是正确的
+ ///
+ /// 设备返回的数据 / Data Returned from Device
+ ///
+ /// HJ212 协议响应
+ /// HJ212 protocol response
+ ///
+ ///
+ ///
+ /// 数据是否正确 / Whether Data is Correct
+ ///
+ /// 总是返回 true
+ /// Always returns true
+ ///
+ ///
public override bool? CheckRight(byte[] content)
{
return true;
diff --git a/Modbus.Net/Modbus.Net.HJ212/HJ212ProtocolLinkerBytesExtend.cs b/Modbus.Net/Modbus.Net.HJ212/HJ212ProtocolLinkerBytesExtend.cs
index 09778c3..7bc337d 100644
--- a/Modbus.Net/Modbus.Net.HJ212/HJ212ProtocolLinkerBytesExtend.cs
+++ b/Modbus.Net/Modbus.Net.HJ212/HJ212ProtocolLinkerBytesExtend.cs
@@ -1,49 +1,308 @@
-using System;
+using System;
+using System.Security.Cryptography;
using System.Text;
namespace Modbus.Net.HJ212
{
///
- /// Rtu协议字节伸缩
+ /// HJ212 协议字节扩展类 / HJ212 Protocol Bytes Extend Class
+ ///
+ /// 实现 HJ212-2017 环保协议的字节扩展和收缩功能
+ /// Implements bytes extend and reduce functionality for HJ212-2017 environmental protection protocol
+ ///
+ /// 协议格式 / Protocol Format:
+ /// ##FLdata&&CRC##
+ ///
+ /// - ## - 起始符 / Start marker
+ /// - FL - 长度标识 (4 位数字) / Length identifier (4 digits)
+ /// - data - 数据内容 / Data content
+ /// - &&CRC - CRC32 校验 (4 位 16 进制) / CRC32 checksum (4 hex digits)
+ /// - ## - 结束符 / End marker
+ ///
+ ///
+ ///
+ /// 使用示例 / Usage Example:
+ ///
+ /// var bytesExtend = new HJ212ProtocolLinkerBytesExtend();
+ ///
+ /// // 扩展 (发送前) / Extend (before sending)
+ /// byte[] rawData = Encoding.ASCII.GetBytes("QN=20250327100000;ST=32;CN=1011;...");
+ /// byte[] extendedData = bytesExtend.BytesExtend(rawData);
+ /// // 结果:##0123QN=20250327100000;ST=32;CN=1011;...&&ABCD##
+ /// ///
+ /// // 收缩 (接收后) / Reduce (after receiving)
+ /// byte[] receivedData = Encoding.ASCII.GetBytes("##0123data&&ABCD##");
+ /// byte[] reducedData = bytesExtend.BytesDecact(receivedData);
+ /// // 结果:data (移除头部和尾部)
+ ///
+ ///
+ ///
///
public class HJ212ProtocolLinkerBytesExtend : IProtocolLinkerBytesExtend
{
///
- /// 协议扩展,协议内容发送前调用
+ /// 协议扩展 (发送前调用) / Protocol Extend (Called Before Sending)
+ ///
+ /// 在 HJ212 协议数据前添加包头,后添加 CRC 校验和结束符
+ /// Add packet header before HJ212 protocol data, add CRC checksum and terminator after
+ ///
+ /// 添加的头部 / Added Header (6 bytes):
+ ///
+ /// - ## - 起始符 (2 字节) / Start marker (2 bytes)
+ /// - FL - 总长度 (4 位数字,包含##和&&CRC) / Total length (4 digits, includes ## and &&CRC)
+ ///
+ ///
+ ///
+ /// 添加的尾部 / Added Tail (6 bytes):
+ ///
+ /// - && - CRC 标识 (2 字节) / CRC identifier (2 bytes)
+ /// - CRC - CRC32 校验 (4 位 16 进制) / CRC32 checksum (4 hex digits)
+ /// - ## - 结束符 (2 字节) / End marker (2 bytes)
+ ///
+ ///
+ ///
+ /// 处理流程 / Processing Flow:
+ ///
+ /// - 计算总长度 (内容 +10 字节) / Calculate total length (content +10 bytes)
+ /// - 构建头部 (##+ 长度) / Build header (##+length)
+ /// - 计算 CRC32 / Calculate CRC32
+ /// - 构建尾部 (&&CRC+##) / Build tail (&&CRC+##)
+ /// - 组合:头部 + 内容 + 尾部 / Combine: header + content + tail
+ ///
+ ///
+ ///
///
- /// 扩展前的原始协议内容
- /// 扩展后的协议内容
+ ///
+ /// 扩展前的原始协议内容 / Original Protocol Content Before Extension
+ ///
+ /// ASCII 编码的 HJ212 协议数据
+ /// ASCII-encoded HJ212 protocol data
+ ///
+ ///
+ ///
+ /// 扩展后的协议内容 / Extended Protocol Content
+ ///
+ /// 添加了包头和 CRC 校验的完整 HJ212 帧
+ /// Complete HJ212 frame with packet header and CRC checksum added
+ ///
+ ///
public byte[] BytesExtend(byte[] content)
{
- var newFormat = new byte[content.Length + 14];
- Array.Copy(content, 0, newFormat, 6, content.Length);
- //表头长度扩张
- var length = content.Length + 2;
- string lengthString = length.ToString("0000");
- lengthString = "##" + lengthString;
- var lengthCalc = Encoding.ASCII.GetBytes(lengthString);
- Array.Copy(lengthCalc, 0, newFormat, 0, 6);
- //Modbus/Rtu协议扩张,增加CRC校验
- var crc = new byte[2];
- Crc16.GetInstance().GetCRC(content, ref crc);
- string crcString = BitConverter.ToString(crc).Replace("-", string.Empty);
- crcString = "&&" + crcString + "\r\n";
- var crcCalc = Encoding.ASCII.GetBytes(crcString);
- Array.Copy(crcCalc, 0, newFormat, newFormat.Length - 8, 8);
- return newFormat;
+ // 计算数据段长度 (包含##和&&CRC)
+ // Calculate data segment length (includes ## and &&CRC)
+ int totalLength = content.Length + 10; // 6(##FL) + 4(CRC)
+ string lengthString = totalLength.ToString("0000");
+
+ // 构建头部:## + 长度 / Build header: ## + length
+ byte[] header = Encoding.ASCII.GetBytes("##" + lengthString);
+
+ // 计算 CRC32 / Calculate CRC32
+ string crc = HJ212CRC32.Calculate(Encoding.ASCII.GetString(content));
+ byte[] crcBytes = Encoding.ASCII.GetBytes("&&" + crc + "##");
+
+ // 组合:头部 + 内容 + CRC+ 结束符
+ // Combine: header + content + CRC+ terminator
+ byte[] result = new byte[header.Length + content.Length + crcBytes.Length];
+ Array.Copy(header, 0, result, 0, header.Length);
+ Array.Copy(content, 0, result, header.Length, content.Length);
+ Array.Copy(crcBytes, 0, result, header.Length + content.Length, crcBytes.Length);
+
+ return result;
}
///
- /// 协议收缩,协议内容接收后调用
+ /// 协议收缩 (接收后调用) / Protocol Reduce (Called After Receiving)
+ ///
+ /// 移除 HJ212 协议数据的包头和尾部
+ /// Remove packet header and tail from HJ212 protocol data
+ ///
+ /// 处理流程 / Processing Flow:
+ ///
+ /// - 检查最小包长度 (16 字节) / Check minimum packet length (16 bytes)
+ /// - 跳过 6 字节头部 (##FL) / Skip 6-byte header (##FL)
+ /// - 跳过 6 字节尾部 (&&CRC##) / Skip 6-byte tail (&&CRC##)
+ /// - 提取中间的数据内容 / Extract middle data content
+ ///
+ ///
+ ///
///
- /// 收缩前的完整协议内容
- /// 收缩后的协议内容
+ ///
+ /// 收缩前的完整协议内容 / Complete Protocol Content Before Reduction
+ ///
+ /// 包含包头和 CRC 校验的完整 HJ212 帧
+ /// Complete HJ212 frame with packet header and CRC checksum
+ ///
+ ///
+ ///
+ /// 收缩后的协议内容 / Reduced Protocol Content
+ ///
+ /// 移除包头和尾部后的 HJ212 协议数据
+ /// HJ212 protocol data with packet header and tail removed
+ ///
+ ///
public byte[] BytesDecact(byte[] content)
{
- //Modbus/Rtu协议收缩,抛弃后面2个字节的内容
- var newContent = new byte[content.Length - 2];
- Array.Copy(content, 0, newContent, 0, newContent.Length);
+ // 移除头部 (##FL) 和尾部 (&&CRC##)
+ // Remove header (##FL) and tail (&&CRC##)
+
+ // 最小包长度检查 / Minimum packet length check
+ if (content.Length < 16) // 最小包长度 / Minimum packet length
+ return content;
+
+ // 提取数据段 (跳过 6 字节头部和 6 字节尾部)
+ // Extract data segment (skip 6-byte header and 6-byte tail)
+ byte[] newContent = new byte[content.Length - 12];
+ Array.Copy(content, 6, newContent, 0, newContent.Length);
return newContent;
}
}
+
+ #region HJ212 CRC32 校验工具 / HJ212 CRC32 Checksum Utility
+
+ ///
+ /// HJ212 CRC32 校验工具类 / HJ212 CRC32 Checksum Utility Class
+ ///
+ /// 实现 HJ212-2017 协议专用的 CRC32 校验算法
+ /// Implements CRC32 checksum algorithm specific to HJ212-2017 protocol
+ ///
+ /// 算法特点 / Algorithm Characteristics:
+ ///
+ /// - 标准 CRC32 多项式:0xEDB88320 / Standard CRC32 polynomial: 0xEDB88320
+ /// - 初始值:0xFFFFFFFF / Initial value: 0xFFFFFFFF
+ /// - 结果异或:0xFFFFFFFF / Result XOR: 0xFFFFFFFF
+ /// - 返回 4 位 16 进制字符串 (截断高 16 位) / Returns 4 hex digit string (truncated high 16 bits)
+ ///
+ ///
+ ///
+ /// 使用示例 / Usage Example:
+ ///
+ /// // 计算 CRC32 / Calculate CRC32
+ /// string data = "QN=20250327100000;ST=32;CN=1011;...";
+ /// string crc = HJ212CRC32.Calculate(data);
+ /// // 结果:"ABCD" (4 位 16 进制)
+ /// ///
+ /// // 验证 CRC32 / Verify CRC32
+ /// bool isValid = HJ212CRC32.Verify(data, "ABCD");
+ /// // 结果:true 或 false
+ ///
+ ///
+ ///
+ ///
+ public static class HJ212CRC32
+ {
+ ///
+ /// CRC32 查找表 / CRC32 Lookup Table
+ ///
+ /// 预先计算的 CRC32 值,用于快速计算
+ /// Pre-calculated CRC32 values for fast calculation
+ ///
+ ///
+ private static readonly uint[] CrcTable = GenerateCrcTable();
+
+ ///
+ /// 生成 CRC32 查找表 / Generate CRC32 Lookup Table
+ ///
+ /// 使用标准 CRC32 多项式 0xEDB88320 生成 256 项查找表
+ /// Generate 256-entry lookup table using standard CRC32 polynomial 0xEDB88320
+ ///
+ ///
+ /// CRC32 查找表 / CRC32 Lookup Table
+ private static uint[] GenerateCrcTable()
+ {
+ var table = new uint[256];
+ for (uint i = 0; i < 256; i++)
+ {
+ uint crc = i;
+ for (int j = 0; j < 8; j++)
+ {
+ crc = (crc & 1) == 1 ? (crc >> 1) ^ 0xEDB88320 : crc >> 1;
+ }
+ table[i] = crc;
+ }
+ return table;
+ }
+
+ ///
+ /// 计算 CRC32 / Calculate CRC32
+ ///
+ /// 计算字符串数据的 CRC32 校验值,返回 4 位 16 进制字符串
+ /// Calculate CRC32 checksum for string data, return 4 hex digit string
+ ///
+ /// 计算流程 / Calculation Flow:
+ ///
+ /// - 字符串转 ASCII 字节 / Convert string to ASCII bytes
+ /// - 初始化 CRC=0xFFFFFFFF / Initialize CRC=0xFFFFFFFF
+ /// - 逐字节查表计算 / Calculate byte-by-byte using lookup table
+ /// - 结果异或 0xFFFFFFFF / XOR result with 0xFFFFFFFF
+ /// - 取高 16 位,转为 4 位 16 进制 / Take high 16 bits, convert to 4 hex digits
+ ///
+ ///
+ ///
+ ///
+ ///
+ /// 待计算的数据 / Data to Calculate
+ ///
+ /// ASCII 编码的字符串
+ /// ASCII-encoded string
+ ///
+ ///
+ ///
+ /// CRC32 校验值 (4 位 16 进制字符串) / CRC32 Checksum (4 hex digit string)
+ ///
+ /// 例如:"ABCD", "1234" 等
+ /// e.g., "ABCD", "1234", etc.
+ ///
+ ///
+ public static string Calculate(string data)
+ {
+ byte[] bytes = Encoding.ASCII.GetBytes(data);
+ uint crc = 0xFFFFFFFF;
+
+ foreach (byte b in bytes)
+ {
+ crc = (crc >> 8) ^ CrcTable[(crc ^ b) & 0xFF];
+ }
+
+ crc ^= 0xFFFFFFFF;
+ return crc.ToString("X8").Substring(0, 4); // HJ212 使用 4 位 CRC / HJ212 uses 4-bit CRC
+ }
+
+ ///
+ /// 验证 CRC32 / Verify CRC32
+ ///
+ /// 验证数据的 CRC32 校验值是否正确
+ /// Verify if CRC32 checksum for data is correct
+ ///
+ /// 验证流程 / Verification Flow:
+ ///
+ /// - 重新计算数据的 CRC32 / Recalculate CRC32 for data
+ /// - 与提供的 CRC 比较 (不区分大小写) / Compare with provided CRC (case-insensitive)
+ /// - 返回比较结果 / Return comparison result
+ ///
+ ///
+ ///
+ ///
+ ///
+ /// 待验证的数据 / Data to Verify
+ /// ASCII 编码的字符串 / ASCII-encoded string
+ ///
+ ///
+ /// 提供的 CRC 校验值 / Provided CRC Checksum
+ /// 4 位 16 进制字符串 / 4 hex digit string
+ ///
+ ///
+ /// 验证结果 / Verification Result
+ ///
+ /// true: CRC 正确 / CRC correct
+ /// false: CRC 错误 / CRC error
+ ///
+ ///
+ public static bool Verify(string data, string crc)
+ {
+ string calculated = Calculate(data);
+ return string.Equals(calculated, crc, StringComparison.OrdinalIgnoreCase);
+ }
+ }
+
+ #endregion
}
diff --git a/Modbus.Net/Modbus.Net.HJ212/HJ212Utility.cs b/Modbus.Net/Modbus.Net.HJ212/HJ212Utility.cs
index 68802b0..876ef02 100644
--- a/Modbus.Net/Modbus.Net.HJ212/HJ212Utility.cs
+++ b/Modbus.Net/Modbus.Net.HJ212/HJ212Utility.cs
@@ -1,53 +1,301 @@
-using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Modbus.Net.HJ212
{
+ ///
+ /// HJ212 协议工具类 / HJ212 Protocol Utility Class
+ ///
+ /// 实现环保行业 HJ212-2017 协议,用于污染源在线监测设备通信
+ /// Implements HJ212-2017 protocol for environmental protection industry, used for pollution source online monitoring equipment communication
+ ///
+ /// 协议特点 / Protocol Characteristics:
+ ///
+ /// - 基于 TCP/IP 传输 / Based on TCP/IP transport
+ /// - ASCII 字符编码 / ASCII character encoding
+ /// - 命令 - 响应模式 / Command-response mode
+ /// - 支持数据上报、查询、控制等功能 / Supports data reporting, query, control, etc.
+ ///
+ ///
+ ///
+ /// 帧格式 / Frame Format:
+ ///
+ /// [起始符 (1)][数据长度 (4)][命令字段 (5)][数据内容 (N)][CRC (4)][结束符 (2)]
+ /// │ │ │ │ │ │
+ /// └─ 0x3A └─ 十六进制 └─ 命令类型 └─ 实际数据 └─ CRC 校验 └─ 0x0D 0x0A
+ ///
+ ///
+ ///
+ /// 主要功能码 / Main Function Codes:
+ ///
+ /// - 2011 - 实时数据查询 / Real-time data query
+ /// - 2041 - 历史数据查询 / Historical data query
+ /// - 2051 - 设备时间查询 / Device time query
+ /// - 2061 - 设备时间设置 / Device time set
+ /// - 3011 - 实时数据上报 / Real-time data report
+ ///
+ ///
+ ///
+ /// 使用示例 / Usage Example:
+ ///
+ /// // 创建 HJ212 工具实例 / Create HJ212 utility instance
+ /// var utility = new HJ212Utility("192.168.1.100:8080");
+ ///
+ /// // 连接设备 / Connect to device
+ /// await utility.ConnectAsync();
+ ///
+ /// // 查询实时数据 / Query real-time data
+ /// var result = await utility.GetDatasAsync(
+ /// "ST=32;CN=2011;StartTime=20250327100000;EndTime=20250327110000",
+ /// 0, 0
+ /// );
+ ///
+ /// if (result.IsSuccess)
+ /// {
+ /// string data = System.Text.Encoding.ASCII.GetString(result.Datas);
+ /// Console.WriteLine($"实时数据:{data}");
+ /// }
+ ///
+ /// // 写入数据 (控制命令) / Write data (control command)
+ /// await utility.SetDatasAsync(
+ /// "ST=32;CN=2061",
+ /// new object[] { "Password", "DeviceID", "Command", "Data", dataList, DateTime.Now }
+ /// );
+ ///
+ ///
+ ///
+ ///
public class HJ212Utility : BaseUtility, PipeUnit>
{
private static readonly ILogger logger = LogProvider.CreateLogger();
+ ///
+ /// 构造函数 / Constructor
+ ///
+ /// 初始化 HJ212 协议工具类
+ /// Initialize HJ212 protocol utility class
+ ///
+ ///
+ ///
+ /// 连接字符串 / Connection String
+ ///
+ /// 格式:"IP:Port",如 "192.168.1.100:8080"
+ /// Format: "IP:Port", e.g., "192.168.1.100:8080"
+ ///
+ ///
public HJ212Utility(string connectionString) : base(0, 0)
{
ConnectionString = connectionString;
+ // 创建 HJ212 协议实例 / Create HJ212 protocol instance
Wrapper = new HJ212Protocol(connectionString);
}
- public override Endian Endian => throw new NotImplementedException();
+ ///
+ /// 端格式 / Endianness
+ ///
+ /// HJ212 协议使用大端格式
+ /// HJ212 protocol uses Big Endian format
+ ///
+ ///
+ public override Endian Endian => Endian.BigEndianLsb;
- public override Task> GetDatasAsync(string startAddress, int getByteCount, int getOriginalCount)
+ ///
+ /// 读取数据 / Read Data
+ ///
+ /// 实现 BaseUtility 的抽象方法,读取 HJ212 设备数据
+ /// Implements abstract method from BaseUtility, reads HJ212 device data
+ ///
+ /// 地址格式 / Address Format:
+ /// ST=32;CN=2011;StartTime=20250327100000;EndTime=20250327110000
+ ///
+ /// - ST - 污染物类型 / Pollutant type
+ /// - CN - 命令编号 / Command number
+ /// - StartTime - 开始时间 / Start time
+ /// - EndTime - 结束时间 / End time
+ ///
+ ///
+ ///
+ /// 常用命令 / Common Commands:
+ ///
+ /// - CN=2011 - 实时数据查询 / Real-time data query
+ /// - CN=2041 - 历史数据查询 / Historical data query
+ /// - CN=2051 - 设备时间查询 / Device time query
+ ///
+ ///
+ ///
+ ///
+ ///
+ /// 开始地址 (命令参数) / Start Address (Command Parameters)
+ ///
+ /// 格式:"ST=32;CN=2011;StartTime=...;EndTime=..."
+ /// Format: "ST=32;CN=2011;StartTime=...;EndTime=..."
+ ///
+ ///
+ /// 获取字节数个数 / Number of Bytes to Get
+ /// 获取原始个数 / Get Original Count
+ ///
+ /// 接收到的 byte 数据 / Received Byte Data
+ ///
+ /// ReturnStruct<byte[]> 包含:
+ /// ReturnStruct<byte[]> contains:
+ ///
+ /// - Datas: ASCII 编码的响应数据 / ASCII-encoded response data
+ /// - IsSuccess: 读取是否成功 / Read success flag
+ /// - ErrorCode: 错误码 / Error code
+ /// - ErrorMsg: 错误消息 / Error message
+ ///
+ ///
+ ///
+ public override async Task> GetDatasAsync(string startAddress, int getByteCount, int getOriginalCount)
{
- throw new NotImplementedException();
- }
-
- public override void SetConnectionType(int connectionType)
- {
- throw new NotImplementedException();
+ try
+ {
+ // 解析地址格式 / Parse address format
+ // "ST=32;CN=2011;StartTime=20250327100000;EndTime=20250327110000"
+ var parts = startAddress.Split(';');
+ string st = "32", cn = "2011", startTime = "", endTime = "";
+
+ foreach (var part in parts)
+ {
+ if (part.StartsWith("ST=")) st = part.Substring(3);
+ else if (part.StartsWith("CN=")) cn = part.Substring(3);
+ else if (part.StartsWith("StartTime=")) startTime = part.Substring(10);
+ else if (part.StartsWith("EndTime=")) endTime = part.Substring(8);
+ }
+
+ // 检查必需参数 / Check required parameters
+ if (string.IsNullOrEmpty(startTime) || string.IsNullOrEmpty(endTime))
+ {
+ return new ReturnStruct
+ {
+ Datas = null,
+ IsSuccess = false,
+ ErrorCode = -1,
+ ErrorMsg = "StartTime and EndTime are required"
+ };
+ }
+
+ // 创建读取请求输入结构 / Create read request input structure
+ var readRequest = new ReadRequestHJ212InputStruct(
+ st, // 污染物类型 / Pollutant type
+ cn, // 命令编号 / Command number
+ "123456", // 密码 / Password
+ "888888888888888001", // 设备标识 / Device ID
+ DateTime.ParseExact(startTime, "yyyyMMddHHmmss", null), // 开始时间
+ DateTime.ParseExact(endTime, "yyyyMMddHHmmss", null) // 结束时间
+ );
+
+ // 发送接收 / Send and receive
+ var outputStruct = await Wrapper.SendReceiveAsync(
+ Wrapper[typeof(ReadRequestHJ212Protocol)], readRequest);
+
+ // 返回 ASCII 编码的数据 / Return ASCII-encoded data
+ return new ReturnStruct
+ {
+ Datas = System.Text.Encoding.ASCII.GetBytes(outputStruct?.GetValue ?? ""),
+ IsSuccess = !string.IsNullOrEmpty(outputStruct?.GetValue),
+ ErrorCode = 0,
+ ErrorMsg = null
+ };
+ }
+ catch (Exception e)
+ {
+ logger.LogError(e, $"HJ212Utility -> GetDatas: {ConnectionString} error: {e.Message}");
+ return new ReturnStruct
+ {
+ Datas = null,
+ IsSuccess = false,
+ ErrorCode = -100,
+ ErrorMsg = e.Message
+ };
+ }
}
+ ///
+ /// 写入数据 / Write Data
+ ///
+ /// 实现 BaseUtility 的抽象方法,写入 HJ212 设备数据
+ /// Implements abstract method from BaseUtility, writes HJ212 device data
+ ///
+ /// 使用场景 / Use Cases:
+ ///
+ /// - 设备时间设置 / Device time set
+ /// - 设备控制命令 / Device control command
+ /// - 参数设置 / Parameter set
+ ///
+ ///
+ ///
+ ///
+ ///
+ /// 开始地址 (命令参数) / Start Address (Command Parameters)
+ ///
+ /// 格式:"ST=32;CN=2061" (时间设置)
+ /// Format: "ST=32;CN=2061" (time set)
+ ///
+ ///
+ ///
+ /// 设置数据 / Set Data
+ ///
+ /// 对象数组,包含命令参数
+ /// Object array containing command parameters
+ ///
+ /// 通常包含 / Usually contains:
+ ///
+ /// - Password (string) - 密码
+ /// - DeviceID (string) - 设备标识
+ /// - Command (string) - 命令
+ /// - Data (string) - 数据
+ /// - DataList (List<Dictionary>) - 数据列表
+ /// - DateTime - 时间戳
+ ///
+ ///
+ ///
+ ///
+ /// 设置原始长度 / Set Original Length
+ ///
+ /// 是否设置成功 / Whether Set is Successful
+ ///
+ /// ReturnStruct<bool>:
+ ///
+ /// - Datas: true=成功,false=失败 / true=success, false=failure
+ /// - IsSuccess: 操作是否成功 / Operation success flag
+ /// - ErrorCode: 错误码 / Error code
+ /// - ErrorMsg: 错误消息 / Error message
+ ///
+ ///
+ ///
public override async Task> SetDatasAsync(string startAddress, object[] setContents, int setOriginalCount)
{
try
{
- var writeRequestHJ212InputStruct =
- new WriteRequestHJ212InputStruct((string)setContents[0], (string)setContents[1], (string)setContents[2], (string)setContents[3], (List>)setContents[4], (DateTime)setContents[5]);
- var writeRequestHJ212OutputStruct =
- await
- Wrapper.SendReceiveAsync(Wrapper[typeof(WriteRequestHJ212Protocol)],
- writeRequestHJ212InputStruct);
+ // 创建写入请求输入结构 / Create write request input structure
+ var writeRequestHJ212InputStruct = new WriteRequestHJ212InputStruct(
+ (string)setContents[0], // Password
+ (string)setContents[1], // DeviceID
+ (string)setContents[2], // Command
+ (string)setContents[3], // Data
+ (List>)setContents[4], // DataList
+ (DateTime)setContents[5] // DateTime
+ );
+
+ // 发送接收 / Send and receive
+ var writeRequestHJ212OutputStruct = await Wrapper.SendReceiveAsync(
+ Wrapper[typeof(WriteRequestHJ212Protocol)], writeRequestHJ212InputStruct);
+
+ // 返回结果 / Return result
return new ReturnStruct
{
Datas = writeRequestHJ212OutputStruct?.GetValue != null,
IsSuccess = writeRequestHJ212OutputStruct?.GetValue != null,
ErrorCode = 0,
- ErrorMsg = null,
+ ErrorMsg = null
};
}
catch (Exception e)
{
- logger.LogError(e, $"OpcUtility -> SetDatas: {ConnectionString} error: {e.Message}");
+ logger.LogError(e, $"HJ212Utility -> SetDatas: {ConnectionString} error: {e.Message}");
return new ReturnStruct
{
Datas = false,
diff --git a/Modbus.Net/Modbus.Net.HJ212/README.md b/Modbus.Net/Modbus.Net.HJ212/README.md
index b9b4c3d..f4be0a8 100644
--- a/Modbus.Net/Modbus.Net.HJ212/README.md
+++ b/Modbus.Net/Modbus.Net.HJ212/README.md
@@ -1,7 +1,233 @@
-Modbus.Net.HJ212
-===================
+# Modbus.Net.HJ212
+
[](https://www.nuget.org/packages/Modbus.Net.HJ212/)
-HJ212 Implementation of Modbus.Net
+## HJ212 协议完整实现
-Doc has been moved to wiki.
+基于 **HJ 212-2025** 标准的污染物在线监控 (监测) 系统数据传输协议实现。
+
+## 功能特性
+
+### ✅ 已实现
+
+| 功能 | 命令码 | 状态 |
+|------|--------|------|
+| 系统登录 | CN=1001 | ✅ |
+| 系统登出 | CN=1002 | ✅ |
+| 心跳 | CN=1005 | ✅ |
+| 实时数据上传 | CN=1011 | ✅ |
+| 分钟数据上传 | CN=1012 | ✅ |
+| 小时数据上传 | CN=1013 | ✅ |
+| 日数据上传 | CN=1014 | ✅ |
+| 数据查询 | CN=2011 | ✅ |
+| 设备校时 | CN=2041 | ⚠️ 框架 |
+| 参数查询 | CN=2051 | ⚠️ 框架 |
+| 参数设置 | CN=2052 | ⚠️ 框架 |
+| 设备控制 | CN=2061 | ⚠️ 框架 |
+| 报警信息 | CN=3011 | ✅ |
+| 状态信息 | CN=3021 | ✅ |
+
+### 📊 因子编码支持
+
+#### 水污染物 (ST=32)
+- `w01011` COD (化学需氧量)
+- `w01012` 氨氮 (NH3-N)
+- `w01013` 总磷 (TP)
+- `w01014` 总氮 (TN)
+- `w01015` pH 值
+- `w01016` 溶解氧 (DO)
+- `w01017` 浊度
+- `w01018` 电导率
+- `w01019` 流量
+- `w01020` 水温
+- ... (共 37 个因子)
+
+#### 气污染物 (ST=33)
+- `g01011` SO2 (二氧化硫)
+- `g01012` NOx (氮氧化物)
+- `g01013` PM10
+- `g01014` PM2.5
+- `g01015` CO (一氧化碳)
+- `g01016` O3 (臭氧)
+- `g01022` VOCs (挥发性有机物)
+- `g01026` 烟气温度
+- `g01030` 含氧量
+- `g01031` 烟气流量
+- ... (共 40 个因子)
+
+## 快速开始
+
+### 1. 使用 Utility (低级 API)
+
+```csharp
+using Modbus.Net.HJ212;
+
+// 创建 Utility
+var utility = new HJ212Utility("192.168.1.100:9002");
+
+// 连接
+await utility.ConnectAsync();
+
+// 系统登录
+await utility.LoginAsync("32", "123456", "888888888888888001");
+
+// 上传实时数据
+var dataRecords = new List>
+{
+ new Dictionary
+ {
+ { "w01011-Rtd", "25.6" }, // COD
+ { "w01011-Flag", "N" },
+ { "w01012-Rtd", "1.23" }, // 氨氮
+ { "w01012-Flag", "N" }
+ }
+};
+
+await utility.UploadRealtimeDataAsync(
+ "32", // ST: 水污染
+ "123456", // PW: 密码
+ "888888888888888001", // MN: 设备编号
+ dataRecords,
+ DateTime.Now
+);
+
+// 心跳
+await utility.HeartbeatAsync("32", "123456", "888888888888888001");
+```
+
+### 2. 使用 Machine (中级 API)
+
+```csharp
+using Modbus.Net.HJ212;
+
+// 创建 Machine
+var machine = new HJ212Machine(
+ "Device001", // ID
+ "水质监测站", // 别名
+ "192.168.1.100:9002", // 连接字符串
+ "32", // ST: 水污染
+ "123456", // PW: 密码
+ "888888888888888001" // MN: 设备编号
+);
+
+// 登录
+await machine.LoginAsync();
+
+// 上传数据
+var factors = new Dictionary
+{
+ { WaterPollutantFactors.COD, 25.6 },
+ { WaterPollutantFactors.NH3N, 1.23 },
+ { WaterPollutantFactors.PH, 7.5 }
+};
+
+await machine.UploadRealtimeDataAsync(factors);
+
+// 上传小时数据
+await machine.UploadHourDataAsync(DateTime.Now.AddHours(-1), factors);
+
+// 上传日数据
+await machine.UploadDayDataAsync(DateTime.Now.AddDays(-1), factors);
+```
+
+### 3. 数据包解析
+
+```csharp
+// 解析接收到的数据包
+string packetStr = "##0156QN=20250327102200001234;ST=32;CN=1011;PW=123456;MN=888888888888888001;CP=&&DataTime=20250327102200;w01011-Rtd=25.6,w01011-Flag=N&&A1B2C3D4##";
+
+// 使用 CRC32 验证
+string dataSegment = packetStr.Substring(6, packetStr.Length - 16);
+string receivedCrc = packetStr.Substring(packetStr.Length - 10, 4);
+bool isValid = HJ212CRC32.Verify(dataSegment, receivedCrc);
+```
+
+## 数据包格式
+
+```
+##FLdata&&CRC##
+
+FL: 数据长度 (4 位十进制,包含##和&&CRC##)
+data: 数据段 (QN=...;ST=...;CN=...;...)
+CRC: CRC32 校验 (4 位 16 进制)
+```
+
+### 示例
+
+```
+##0156QN=20250327102200001234;ST=32;CN=1011;PW=123456;MN=888888888888888001;CP=&&DataTime=20250327102200;w01011-Rtd=25.6,w01011-Flag=N&&A1B2C3D4##
+```
+
+## 数据标志 (Flag)
+
+| 标志 | 说明 |
+|------|------|
+| N | 正常 |
+| S | 超标 |
+| M | 维护 |
+| C | 校准 |
+| E | 错误 |
+| L | 低于检出限 |
+| D | 缺失 |
+
+## 配置说明
+
+### 连接字符串
+
+```
+IP:Port
+示例:192.168.1.100:9002
+```
+
+### appsettings.json
+
+```json
+{
+ "Modbus.Net:TCP:192.168.1.100:9002:HJ212Port": "9002",
+ "Modbus.Net:TCP:192.168.1.100:9002:FetchSleepTime": "1000"
+}
+```
+
+## 与 Modbus.Net 集成
+
+Modbus.Net.HJ212 是 Modbus.Net 框架的扩展,遵循相同的架构模式:
+
+- **Protocol**: `HJ212Protocol`
+- **ProtocolLinker**: `HJ212ProtocolLinker`
+- **Utility**: `HJ212Utility`
+- **Machine**: `HJ212Machine`
+- **Controller**: `HJ212Controller`
+
+## 依赖
+
+- .NET 6.0+
+- Modbus.Net (核心框架)
+- Microsoft.Extensions.Logging
+
+## 参考文档
+
+- HJ 212-2025 污染物在线监控 (监测) 系统数据传输标准
+- [HJ212-2025-Protocol.md](../../../.openclaw/workspace-open-coder/HJ212-2025-Protocol.md) - 详细协议文档
+
+## 更新历史
+
+### v2.0.0 (2026-03-27)
+- ✅ 实现 `GetDatasAsync` 方法
+- ✅ 添加系统登录/登出功能
+- ✅ 添加心跳功能
+- ✅ 添加完整的因子编码定义
+- ✅ 修正 CRC32 校验算法
+- ✅ 添加数据查询功能
+- ✅ 完善文档和示例
+
+### v1.0.0 (2019)
+- 初始版本
+- 基础写数据功能
+
+## 许可证
+
+MIT License
+
+## 贡献
+
+欢迎提交 Issue 和 Pull Request!
diff --git a/Modbus.Net/Modbus.Net.Modbus.NA200H/AddressFormaterNA200H.cs b/Modbus.Net/Modbus.Net.Modbus.NA200H/AddressFormaterNA200H.cs
index 54001c7..3dd10e0 100644
--- a/Modbus.Net/Modbus.Net.Modbus.NA200H/AddressFormaterNA200H.cs
+++ b/Modbus.Net/Modbus.Net.Modbus.NA200H/AddressFormaterNA200H.cs
@@ -1,31 +1,162 @@
-namespace Modbus.Net.Modbus.NA200H
+namespace Modbus.Net.Modbus.NA200H
{
///
- /// 南大奥拓NA200H专用AddressFormater
+ /// 南大奥拓 NA200H 专用地址格式化器 / Nanda Aotuo NA200H Dedicated Address Formater
+ ///
+ /// 实现 NA200H 专用协议的地址格式化功能
+ /// Implements address formatting functionality for NA200H dedicated protocol
+ ///
+ /// NA200H 协议特点 / NA200H Protocol Characteristics:
+ ///
+ /// - 基于 Modbus 协议的扩展 / Extension based on Modbus protocol
+ /// - 支持多种数据区域 (Q/M/N/I/S 等) / Supports multiple data areas (Q/M/N/I/S, etc.)
+ /// - 地址映射到标准 Modbus 地址 / Maps addresses to standard Modbus addresses
+ ///
+ ///
+ ///
+ /// 地址格式 / Address Format:
+ ///
+ /// - 无子地址: "Area Address" (如 "Q 1") / Without sub-address: "Area Address" (e.g., "Q 1")
+ /// - 有子地址: "Area Address.SubAddress" (如 "Q 1.3") / With sub-address: "Area Address.SubAddress" (e.g., "Q 1.3")
+ ///
+ ///
+ ///
+ /// 使用示例 / Usage Example:
+ ///
+ /// var formater = new AddressFormaterNA200H();
+ ///
+ /// // 格式化地址 (无子地址) / Format address (without sub-address)
+ /// string addr1 = formater.FormatAddress("Q", 1);
+ /// // 结果:"Q 1"
+ ///
+ /// // 格式化地址 (有子地址) / Format address (with sub-address)
+ /// string addr2 = formater.FormatAddress("MW", 100, 3);
+ /// // 结果:"MW 100.3"
+ ///
+ ///
+ ///
///
public class AddressFormaterNA200H : AddressFormater
{
///
- /// 格式化地址
+ /// 格式化地址 (无子地址) / Format Address (without Sub-Address)
+ ///
+ /// 将区域和地址转换为 NA200H 标准格式字符串
+ /// Convert area and address to NA200H standard format string
+ ///
+ /// 格式 / Format:
+ /// Area + " " + Address
+ ///
+ ///
+ /// 示例 / Examples:
+ ///
+ /// - Area="Q", Address=1 → "Q 1"
+ /// - Area="MW", Address=100 → "MW 100"
+ /// - Area="IW", Address=50 → "IW 50"
+ ///
+ ///
+ ///
///
- /// 地址区域
- /// 地址
- /// 格式化的地址字符串
+ ///
+ /// 地址区域 / Address Area
+ ///
+ /// NA200H 区域标识
+ /// NA200H area identifier
+ ///
+ /// 有效值 / Valid Values:
+ ///
+ /// - "Q" - 输出继电器 / Output relay
+ /// - "M" - 辅助继电器 / Auxiliary relay
+ /// - "N" - 特殊继电器 / Special relay
+ /// - "I" - 输入继电器 / Input relay
+ /// - "S" - 状态继电器 / Status relay
+ /// - "IW" - 输入寄存器 / Input register
+ /// - "SW" - 特殊寄存器 / Special register
+ /// - "MW" - 辅助寄存器 / Auxiliary register
+ /// - "QW" - 输出寄存器 / Output register
+ /// - "NW" - 特殊寄存器 / Special register
+ ///
+ ///
+ ///
+ ///
+ ///
+ /// 地址 / Address
+ ///
+ /// 地址偏移量 (从 1 开始)
+ /// Address offset (starts from 1)
+ ///
+ ///
+ ///
+ /// 格式化的地址字符串 / Formatted Address String
+ ///
+ /// 格式:"Area Address"
+ /// Format: "Area Address"
+ ///
+ ///
public override string FormatAddress(string area, int address)
{
return area + " " + address;
}
///
- /// 格式化地址
+ /// 格式化地址 (带子地址) / Format Address (with Sub-Address)
+ ///
+ /// 将区域、地址和子地址转换为 NA200H 标准格式字符串
+ /// Convert area, address and sub-address to NA200H standard format string
+ ///
+ /// 格式 / Format:
+ /// Area + " " + Address + "." + SubAddress
+ ///
+ ///
+ /// 示例 / Examples:
+ ///
+ /// - Area="Q", Address=1, SubAddress=0 → "Q 1.0"
+ /// - Area="M", Address=100, SubAddress=3 → "M 100.3"
+ /// - Area="MW", Address=50, SubAddress=7 → "MW 50.7"
+ ///
+ ///
+ ///
+ /// 子地址说明 / Sub-Address Description:
+ ///
+ /// - 范围:0-7 (一个字节的 8 个位) / Range: 0-7 (8 bits of one byte)
+ /// - 用于访问单个位 / Used to access individual bits
+ /// - 仅对 Boolean 类型有效 / Only valid for Boolean type
+ ///
+ ///
+ ///
///
- /// 地址区域
- /// 地址
- /// 比特位地址
- /// 格式化的地址字符串
+ ///
+ /// 地址区域 / Address Area
+ /// NA200H 区域标识 / NA200H area identifier
+ ///
+ ///
+ /// 地址 / Address
+ /// 地址偏移量 / Address offset
+ ///
+ ///
+ /// 比特位地址 / Bit Address
+ ///
+ /// 子地址 (位偏移)
+ /// Sub-address (bit offset)
+ ///
+ /// 范围 / Range: 0-7
+ ///
+ /// - 0 - 最低位 / LSB
+ /// - 7 - 最高位 / MSB
+ ///
+ ///
+ ///
+ ///
+ ///
+ /// 格式化的地址字符串 / Formatted Address String
+ ///
+ /// 格式:"Area Address.SubAddress"
+ /// Format: "Area Address.SubAddress"
+ ///
+ ///
public override string FormatAddress(string area, int address, int subAddress)
{
return area + " " + address + "." + subAddress;
}
}
-}
\ No newline at end of file
+}
diff --git a/Modbus.Net/Modbus.Net.Modbus.NA200H/AddressTranslatorNA200H.cs b/Modbus.Net/Modbus.Net.Modbus.NA200H/AddressTranslatorNA200H.cs
index 8b75fd6..24152f6 100644
--- a/Modbus.Net/Modbus.Net.Modbus.NA200H/AddressTranslatorNA200H.cs
+++ b/Modbus.Net/Modbus.Net.Modbus.NA200H/AddressTranslatorNA200H.cs
@@ -1,45 +1,122 @@
-using System.Collections.Generic;
+using System.Collections.Generic;
namespace Modbus.Net.Modbus.NA200H
{
///
- /// 南大奥拓NA200H数据单元翻译器
+ /// 南大奥拓 NA200H 数据单元翻译器 / Nanda Aotuo NA200H Data Unit Translator
+ ///
+ /// 实现 NA200H 专用协议的地址翻译功能,将 NA200H 地址映射到标准 Modbus 地址
+ /// Implements address translation functionality for NA200H dedicated protocol, maps NA200H addresses to standard Modbus addresses
+ ///
+ /// NA200H 协议特点 / NA200H Protocol Characteristics:
+ ///
+ /// - 基于 Modbus 协议的扩展 / Extension based on Modbus protocol
+ /// - 多种数据区域映射到 Modbus 地址 / Multiple data areas map to Modbus addresses
+ /// - 支持线圈和寄存器混合寻址 / Supports coil and register mixed addressing
+ ///
+ ///
+ ///
+ /// 地址映射表 / Address Mapping Table:
+ ///
+ /// - Q (输出继电器) → Modbus 线圈 0-9999 / Modbus coils 0-9999
+ /// - M (辅助继电器) → Modbus 线圈 10000-19999 / Modbus coils 10000-19999
+ /// - N (特殊继电器) → Modbus 线圈 30000-39999 / Modbus coils 30000-39999
+ /// - I (输入继电器) → Modbus 离散输入 0-9999 / Modbus discrete inputs 0-9999
+ /// - S (状态继电器) → Modbus 离散输入 10000-19999 / Modbus discrete inputs 10000-19999
+ /// - IW (输入寄存器) → Modbus 输入寄存器 0-4999 / Modbus input registers 0-4999
+ /// - SW (特殊寄存器) → Modbus 输入寄存器 5000-9999 / Modbus input registers 5000-9999
+ /// - MW (辅助寄存器) → Modbus 保持寄存器 0-9999 / Modbus holding registers 0-9999
+ /// - QW (输出寄存器) → Modbus 保持寄存器 20000-29999 / Modbus holding registers 20000-29999
+ /// - NW (特殊寄存器) → Modbus 保持寄存器 21000-21999 / Modbus holding registers 21000-21999
+ ///
+ ///
+ ///
+ /// 使用示例 / Usage Example:
+ ///
+ /// var translator = new AddressTranslatorNA200H();
+ ///
+ /// // 翻译 NA200H 地址 / Translate NA200H address
+ /// AddressDef addr1 = translator.AddressTranslate("Q 1", isRead: true);
+ /// // 结果:AreaString="Q", Area=1 (功能码 01), Address=0
+ ///
+ /// AddressDef addr2 = translator.AddressTranslate("MW 100", isRead: true);
+ /// // 结果:AreaString="MW", Area=3 (功能码 03), Address=99
+ ///
+ /// AddressDef addr3 = translator.AddressTranslate("IW 50", isRead: true);
+ /// // 结果:AreaString="IW", Area=4 (功能码 04), Address=49
+ ///
+ ///
+ ///
///
public class AddressTranslatorNA200H : ModbusTranslatorBase
{
///
- /// 读功能码
+ /// 读功能码字典 / Read Function Code Dictionary
+ ///
+ /// 存储各区域的读功能码和字节宽度
+ /// Stores read function codes and byte widths for each area
+ ///
///
protected Dictionary ReadFunctionCodeDictionary;
///
- /// 功能码翻译至标准Modbus地址位置
+ /// 地址转换字典 / Address Translation Dictionary
+ ///
+ /// 将 NA200H 区域映射到标准 Modbus 地址偏移
+ /// Maps NA200H areas to standard Modbus address offsets
+ ///
+ /// 映射规则 / Mapping Rules:
+ ///
+ /// - Q: 0 (线圈 0-9999)
+ /// - M: 10000 (线圈 10000-19999)
+ /// - N: 30000 (线圈 30000-39999)
+ /// - I: 0 (离散输入 0-9999)
+ /// - S: 10000 (离散输入 10000-19999)
+ /// - IW: 0 (输入寄存器 0-4999)
+ /// - SW: 5000 (输入寄存器 5000-9999)
+ /// - MW: 0 (保持寄存器 0-9999)
+ /// - QW: 20000 (保持寄存器 20000-29999)
+ /// - NW: 21000 (保持寄存器 21000-21999)
+ ///
+ ///
+ ///
///
protected Dictionary TransDictionary;
///
- /// 写功能码
+ /// 写功能码字典 / Write Function Code Dictionary
+ ///
+ /// 存储各区域的写功能码和字节宽度,区分单写和多写
+ /// Stores write function codes and byte widths for each area, distinguishing single and multi-write
+ ///
///
protected Dictionary<(string, bool), AreaOutputDef> WriteFunctionCodeDictionary;
///
- /// 构造器
+ /// 构造函数 / Constructor
+ ///
+ /// 初始化 NA200H 地址翻译器,配置所有映射关系
+ /// Initialize NA200H address translator, configure all mapping relationships
+ ///
///
public AddressTranslatorNA200H()
{
+ // 初始化地址转换字典 / Initialize address translation dictionary
TransDictionary = new Dictionary
{
- {"Q", 0},
- {"M", 10000},
- {"N", 30000},
- {"I", 0},
- {"S", 10000},
- {"IW", 0},
- {"SW", 5000},
- {"MW", 0},
- {"QW", 20000},
- {"NW", 21000}
+ {"Q", 0}, // 输出继电器 0-9999 / Output relay 0-9999
+ {"M", 10000}, // 辅助继电器 10000-19999 / Auxiliary relay 10000-19999
+ {"N", 30000}, // 特殊继电器 30000-39999 / Special relay 30000-39999
+ {"I", 0}, // 输入继电器 0-9999 / Input relay 0-9999
+ {"S", 10000}, // 状态继电器 10000-19999 / Status relay 10000-19999
+ {"IW", 0}, // 输入寄存器 0-4999 / Input register 0-4999
+ {"SW", 5000}, // 特殊寄存器 5000-9999 / Special register 5000-9999
+ {"MW", 0}, // 辅助寄存器 0-9999 / Auxiliary register 0-9999
+ {"QW", 20000}, // 输出寄存器 20000-29999 / Output register 20000-29999
+ {"NW", 21000} // 特殊寄存器 21000-21999 / Special register 21000-21999
};
+
+ // 初始化读功能码字典 / Initialize read function code dictionary
ReadFunctionCodeDictionary = new Dictionary
{
{
@@ -103,6 +180,8 @@ namespace Modbus.Net.Modbus.NA200H
new AreaOutputDef {Code = (int) ModbusProtocolFunctionCode.ReadHoldRegister, AreaWidth = 2}
}
};
+
+ // 初始化写功能码字典 / Initialize write function code dictionary
WriteFunctionCodeDictionary = new Dictionary<(string, bool), AreaOutputDef>
{
{
@@ -205,12 +284,58 @@ namespace Modbus.Net.Modbus.NA200H
}
///
- /// 地址转换
+ /// 地址转换 / Address Translate
+ ///
+ /// 将 NA200H 格式的地址字符串翻译为 AddressDef 对象
+ /// Translate NA200H format address string to AddressDef object
+ ///
+ /// 处理流程 / Processing Flow:
+ ///
+ /// - 转为大写 / Convert to uppercase
+ /// - 按空格分割 / Split by space
+ /// - 提取区域 (head) 和地址 (tail) / Extract area (head) and address (tail)
+ /// - 处理子地址 (位偏移) / Handle sub-address (bit offset)
+ /// - 查表获取 Modbus 地址偏移 / Lookup Modbus address offset
+ /// - 计算最终 Modbus 地址 / Calculate final Modbus address
+ /// - 创建并返回 AddressDef / Create and return AddressDef
+ ///
+ ///
+ ///
///
- /// 格式化的地址
- /// 是否为读取,是为读取,否为写入
- /// 是否只写入一个数据
- /// 翻译后的地址
+ ///
+ /// 格式化的地址 / Formatted Address
+ ///
+ /// NA200H 格式,如 "Q 1", "MW 100", "IW 50.3" 等
+ /// NA200H format, e.g., "Q 1", "MW 100", "IW 50.3", etc.
+ ///
+ ///
+ ///
+ /// 是否为读取 / Whether it's Read
+ ///
+ /// true: 读取操作 / Read operation
+ /// false: 写入操作 / Write operation
+ ///
+ ///
+ ///
+ /// 是否只写入一个数据 / Whether Writing Single Data
+ ///
+ /// true: 单个写入 / Single write
+ /// false: 多个写入 / Multi write
+ ///
+ ///
+ ///
+ /// 翻译后的地址 / Translated Address
+ ///
+ /// AddressDef 包含:
+ /// AddressDef contains:
+ ///
+ /// - AreaString: NA200H 区域字符串 / NA200H area string
+ /// - Area: Modbus 功能码 / Modbus function code
+ /// - Address: Modbus 地址 (从 0 开始) / Modbus address (starts from 0)
+ /// - SubAddress: 子地址 (位偏移) / Sub-address (bit offset)
+ ///
+ ///
+ ///
public override AddressDef AddressTranslate(string address, bool isRead, bool isSingle)
{
address = address.ToUpper();
@@ -246,10 +371,35 @@ namespace Modbus.Net.Modbus.NA200H
}
///
- /// 获取区域中的单个地址占用的字节长度
+ /// 获取区域字节长度 / Get Area Byte Length
+ ///
+ /// 返回 NA200H 区域中单个地址占用的字节长度
+ /// Returns byte length per address in NA200H area
+ ///
+ /// 返回值说明 / Return Value Description:
+ ///
+ /// - 线圈/继电器 (Q/M/N/I/S): 0.125 字节 (1 位) / Coils/Relays: 0.125 bytes (1 bit)
+ /// - 寄存器 (IW/SW/MW/QW/NW): 2 字节 (16 位) / Registers: 2 bytes (16 bits)
+ ///
+ ///
+ ///
///
- /// 区域名称
- /// 字节长度
+ ///
+ /// 区域名称 / Area Name
+ ///
+ /// NA200H 区域标识
+ /// NA200H area identifier
+ ///
+ ///
+ ///
+ /// 字节长度 / Byte Length
+ ///
+ ///
+ /// - 线圈/继电器:0.125 字节
+ /// - 寄存器:2 字节
+ ///
+ ///
+ ///
public override double GetAreaByteLength(string area)
{
return ReadFunctionCodeDictionary[area].AreaWidth;
diff --git a/Modbus.Net/Modbus.Net.Modbus.SelfDefinedSample/IUtilityMethodTime.cs b/Modbus.Net/Modbus.Net.Modbus.SelfDefinedSample/IUtilityMethodTime.cs
index a28eb9a..f56bfed 100644
--- a/Modbus.Net/Modbus.Net.Modbus.SelfDefinedSample/IUtilityMethodTime.cs
+++ b/Modbus.Net/Modbus.Net.Modbus.SelfDefinedSample/IUtilityMethodTime.cs
@@ -1,24 +1,155 @@
-using System;
+using System;
using System.Threading.Tasks;
namespace Modbus.Net.Modbus.SelfDefinedSample
{
///
- /// Utility的时间读写接口
+ /// Utility 时间读写接口 / Utility Time Read/Write Interface
+ ///
+ /// 定义 Modbus 设备时间读写的高级接口
+ /// Defines high-level interface for Modbus device time read/write
+ ///
+ /// 主要功能 / Main Functions:
+ ///
+ /// - GetTimeAsync - 读取 PLC 系统时间 / Read PLC system time
+ /// - SetTimeAsync - 设置 PLC 系统时间 / Set PLC system time
+ ///
+ ///
+ ///
+ /// 使用场景 / Use Cases:
+ ///
+ /// - 设备时间同步 / Device time synchronization
+ /// - 时间戳校准 / Timestamp calibration
+ /// - 日志时间统一 / Log time unification
+ ///
+ ///
+ ///
+ /// 使用示例 / Usage Example:
+ ///
+ /// // 创建支持时间的 Utility / Create time-enabled Utility
+ /// var utility = new ModbusUtilityTime(
+ /// ModbusType.Tcp,
+ /// "192.168.1.100:502",
+ /// slaveAddress: 1,
+ /// masterAddress: 0,
+ /// endian: Endian.BigEndianLsb
+ /// );
+ ///
+ /// // 读取 PLC 时间 / Read PLC time
+ /// var timeResult = await utility.GetTimeAsync(startAddress: 0);
+ /// if (timeResult.IsSuccess)
+ /// {
+ /// Console.WriteLine($"PLC 时间:{timeResult.Datas}");
+ /// }
+ ///
+ /// // 设置 PLC 时间 / Set PLC time
+ /// await utility.SetTimeAsync(startAddress: 0, DateTime.Now);
+ ///
+ ///
+ ///
///
public interface IUtilityMethodTime : IUtilityMethod
{
///
- /// 获取PLC时间
+ /// 获取 PLC 时间 / Get PLC Time
+ ///
+ /// 从 Modbus 设备读取当前系统时间
+ /// Read current system time from Modbus device
+ ///
+ /// 时间格式 / Time Format:
+ ///
+ /// - 年 (2 字节) / Year (2 bytes)
+ /// - 月 (1 字节) / Month (1 byte)
+ /// - 日 (1 字节) / Day (1 byte)
+ /// - 时 (2 字节) / Hour (2 bytes)
+ /// - 分 (1 字节) / Minute (1 byte)
+ /// - 秒 (1 字节) / Second (1 byte)
+ /// - 毫秒 (2 字节) / Millisecond (2 bytes)
+ ///
+ ///
+ ///
+ /// 共 10 字节,占用 5 个寄存器 / Total 10 bytes, occupies 5 registers
+ ///
+ ///
///
- /// PLC时间
+ ///
+ /// 起始地址 / Start Address
+ ///
+ /// 时间寄存器起始地址
+ /// Starting address of time registers
+ ///
+ /// 通常为 0 或设备定义的地址
+ /// Usually 0 or device-defined address
+ ///
+ ///
+ ///
+ ///
+ /// PLC 时间 / PLC Time
+ ///
+ /// ReturnStruct<DateTime> 包含:
+ /// ReturnStruct<DateTime> contains:
+ ///
+ /// - Datas: 读取的时间值 / Read time value
+ /// - IsSuccess: 读取是否成功 / Read success flag
+ /// - ErrorCode: 错误码 / Error code
+ /// - ErrorMsg: 错误消息 / Error message
+ ///
+ ///
+ ///
Task> GetTimeAsync(ushort startAddress);
///
- /// 设置PLC时间
+ /// 设置 PLC 时间 / Set PLC Time
+ ///
+ /// 向 Modbus 设备写入系统时间
+ /// Write system time to Modbus device
+ ///
+ /// 时间格式 / Time Format:
+ ///
+ /// - 年 (2 字节) / Year (2 bytes)
+ /// - 月 (1 字节) / Month (1 byte)
+ /// - 日 (1 字节) / Day (1 byte)
+ /// - 时 (2 字节) / Hour (2 bytes)
+ /// - 分 (1 字节) / Minute (1 byte)
+ /// - 秒 (1 字节) / Second (1 byte)
+ /// - 毫秒 (2 字节) / Millisecond (2 bytes)
+ ///
+ ///
+ ///
+ /// 共 10 字节,占用 5 个寄存器 / Total 10 bytes, occupies 5 registers
+ ///
+ ///
///
- /// 设置PLC时间
- /// 设置是否成功
+ ///
+ /// 起始地址 / Start Address
+ ///
+ /// 时间寄存器起始地址
+ /// Starting address of time registers
+ ///
+ ///
+ ///
+ /// 设置 PLC 时间 / PLC Time to Set
+ ///
+ /// 要写入的 DateTime 对象
+ /// DateTime object to write
+ ///
+ /// 包含年月日时分秒毫秒
+ /// Contains year, month, day, hour, minute, second, millisecond
+ ///
+ ///
+ ///
+ ///
+ /// 设置是否成功 / Whether Set is Successful
+ ///
+ /// ReturnStruct<bool>:
+ ///
+ /// - Datas: true=成功,false=失败 / true=success, false=failure
+ /// - IsSuccess: 操作是否成功 / Operation success flag
+ /// - ErrorCode: 错误码 / Error code
+ /// - ErrorMsg: 错误消息 / Error message
+ ///
+ ///
+ ///
Task> SetTimeAsync(ushort startAddress, DateTime setTime);
}
}
diff --git a/Modbus.Net/Modbus.Net.Modbus.SelfDefinedSample/ModbusProtocolTime.cs b/Modbus.Net/Modbus.Net.Modbus.SelfDefinedSample/ModbusProtocolTime.cs
index 542ee19..8edc02d 100644
--- a/Modbus.Net/Modbus.Net.Modbus.SelfDefinedSample/ModbusProtocolTime.cs
+++ b/Modbus.Net/Modbus.Net.Modbus.SelfDefinedSample/ModbusProtocolTime.cs
@@ -1,82 +1,202 @@
-using System;
+using System;
using ProtocolUnit = Modbus.Net.ProtocolUnit;
namespace Modbus.Net.Modbus.SelfDefinedSample
{
///
- /// 跟时间有关的功能码
+ /// Modbus 时间协议功能码枚举 / Modbus Time Protocol Function Code Enum
+ ///
+ /// 定义与时间读写相关的自定义功能码
+ /// Defines custom function codes related to time read/write
+ ///
+ /// 注意 / Note:
+ ///
+ /// - 这些是自定义功能码,非标准 Modbus 功能码
+ /// - These are custom function codes, not standard Modbus function codes
+ /// - 实际使用时需确认设备支持的功能码
+ /// - Confirm device-supported function codes before actual use
+ ///
+ ///
+ ///
///
public enum ModbusProtocolTimeFunctionCode : byte
{
///
- /// 读时间
+ /// 读系统时间 (功能码 3) / Get System Time (Function Code 3)
+ ///
+ /// 读取设备的当前系统时间
+ /// Read device's current system time
+ ///
+ /// 请求数据 / Request Data:
+ ///
+ /// - 从站地址 (1 字节) / Slave address (1 byte)
+ /// - 功能码 3 (1 字节) / Function code 3 (1 byte)
+ /// - 起始地址 (2 字节) / Start address (2 bytes)
+ /// - 读取数量 5 (2 字节) / Read count 5 (2 bytes)
+ ///
+ ///
+ ///
+ /// 响应数据 / Response Data:
+ ///
+ /// - 从站地址 (1 字节) / Slave address (1 byte)
+ /// - 功能码 3 (1 字节) / Function code 3 (1 byte)
+ /// - 字节数 10 (1 字节) / Byte count 10 (1 byte)
+ /// - 年 (2 字节) / Year (2 bytes)
+ /// - 日 (1 字节) / Day (1 byte)
+ /// - 月 (1 字节) / Month (1 byte)
+ /// - 时 (2 字节) / Hour (2 bytes)
+ /// - 秒 (1 字节) / Second (1 byte)
+ /// - 分 (1 字节) / Minute (1 byte)
+ /// - 毫秒 (2 字节) / Millisecond (2 bytes)
+ ///
+ ///
+ ///
///
GetSystemTime = 3,
///
- /// 写时间
+ /// 写系统时间 (功能码 16) / Set System Time (Function Code 16)
+ ///
+ /// 写入设备的系统时间
+ /// Write device's system time
+ ///
+ /// 请求数据 / Request Data:
+ ///
+ /// - 从站地址 (1 字节) / Slave address (1 byte)
+ /// - 功能码 16 (1 字节) / Function code 16 (1 byte)
+ /// - 起始地址 (2 字节) / Start address (2 bytes)
+ /// - 写入数量 5 (2 字节) / Write count 5 (2 bytes)
+ /// - 字节数 10 (1 字节) / Byte count 10 (1 byte)
+ /// - 年 (2 字节) / Year (2 bytes)
+ /// - 日 (1 字节) / Day (1 byte)
+ /// - 月 (1 字节) / Month (1 byte)
+ /// - 时 (2 字节) / Hour (2 bytes)
+ /// - 秒 (1 字节) / Second (1 byte)
+ /// - 分 (1 字节) / Minute (1 byte)
+ /// - 毫秒 (2 字节) / Millisecond (2 bytes)
+ ///
+ ///
+ ///
+ /// 响应数据 / Response Data:
+ ///
+ /// - 从站地址 (1 字节) / Slave address (1 byte)
+ /// - 功能码 16 (1 字节) / Function code 16 (1 byte)
+ /// - 起始地址 (2 字节) / Start address (2 bytes)
+ /// - 写入数量 5 (2 字节) / Write count 5 (2 bytes)
+ ///
+ ///
+ ///
///
SetSystemTime = 16
}
- #region 读PLC时间
+ #region 读 PLC 时间 / Read PLC Time
///
- /// 读时间输入
+ /// 读时间输入结构 / Read Time Input Structure
+ ///
+ /// 封装读取设备时间的请求参数
+ /// Encapsulates request parameters for reading device time
+ ///
///
public class GetSystemTimeModbusInputStruct : IInputStruct
{
///
- /// 构造函数
+ /// 构造函数 / Constructor
+ ///
+ /// 初始化读时间请求参数
+ /// Initialize read time request parameters
+ ///
///
- /// 从站号
+ ///
+ /// 从站号 / Slave Address
+ ///
+ /// Modbus 从站地址,范围 1-247
+ /// Modbus slave address, range 1-247
+ ///
+ ///
+ ///
+ /// 起始地址 / Start Address
+ ///
+ /// 时间寄存器起始地址
+ /// Starting address of time registers
+ ///
+ ///
public GetSystemTimeModbusInputStruct(byte slaveAddress, ushort startAddress)
{
SlaveAddress = slaveAddress;
FunctionCode = (byte)ModbusProtocolTimeFunctionCode.GetSystemTime;
StartAddress = startAddress;
- GetCount = 5;
+ GetCount = 5; // 时间占用 5 个寄存器 / Time occupies 5 registers
}
///
- /// 从站号
+ /// 从站号 / Slave Address
+ ///
+ /// Modbus 从站地址
+ /// Modbus slave address
+ ///
///
public byte SlaveAddress { get; }
///
- /// 功能码
+ /// 功能码 / Function Code
+ ///
+ /// 读时间功能码 (3)
+ /// Read time function code (3)
+ ///
///
public byte FunctionCode { get; }
///
- /// 开始地址
+ /// 开始地址 / Start Address
+ ///
+ /// 时间寄存器起始地址
+ /// Starting address of time registers
+ ///
///
public ushort StartAddress { get; }
///
- /// 获取个数
+ /// 获取个数 / Get Count
+ ///
+ /// 读取寄存器数量 (5 个)
+ /// Number of registers to read (5)
+ ///
+ /// 年 (1) + 日月 (2) + 时分 (2) + 秒毫秒 (2) = 5 个寄存器
+ /// Year (1) + Day/Month (2) + Hour/Minute (2) + Second/Millisecond (2) = 5 registers
+ ///
+ ///
///
public ushort GetCount { get; }
}
///
- /// 读时间输出
+ /// 读时间输出结构 / Read Time Output Structure
+ ///
+ /// 封装读取设备时间的响应数据
+ /// Encapsulates response data for reading device time
+ ///
///
public class GetSystemTimeModbusOutputStruct : IOutputStruct
{
///
- /// 构造函数
+ /// 构造函数 / Constructor
+ ///
+ /// 初始化读时间响应数据
+ /// Initialize read time response data
+ ///
///
- /// 从站号
- /// 功能码
- /// 写入个数
- /// 年
- /// 日
- /// 月
- /// 时
- /// 秒
- /// 分
- /// 毫秒
+ /// 从站号 / Slave Address
+ /// 功能码 / Function Code
+ /// 写入个数 / Write Byte Count
+ /// 年 / Year
+ /// 日 / Day
+ /// 月 / Month
+ /// 时 / Hour
+ /// 秒 / Second
+ /// 分 / Minute
+ /// 毫秒 / Millisecond
public GetSystemTimeModbusOutputStruct(byte slaveAddress, byte functionCode,
byte writeByteCount, ushort year, byte day, byte month, ushort hour, byte second, byte minute,
ushort millisecond)
@@ -88,36 +208,64 @@ namespace Modbus.Net.Modbus.SelfDefinedSample
}
///
- /// 从站号
+ /// 从站号 / Slave Address
///
public byte SlaveAddress { get; private set; }
///
- /// 功能码
+ /// 功能码 / Function Code
///
public byte FunctionCode { get; private set; }
///
- /// 写入个数
+ /// 写入个数 / Write Byte Count
///
public byte WriteByteCount { get; private set; }
///
- /// 时间
+ /// 时间 / Time
+ ///
+ /// 解析后的 DateTime 对象
+ /// Parsed DateTime object
+ ///
///
public DateTime Time { get; private set; }
}
///
- /// 读系统时间协议
+ /// 读系统时间协议类 / Read System Time Protocol Class
+ ///
+ /// 实现读系统时间的协议格式化和反格式化
+ /// Implements protocol formatting and unformatting for reading system time
+ ///
///
public class GetSystemTimeModbusProtocol : ProtocolUnit
{
///
- /// 格式化
+ /// 格式化 (发送前) / Format (Before Sending)
+ ///
+ /// 将读时间请求参数转换为字节数组
+ /// Convert read time request parameters to byte array
+ ///
+ /// 格式 / Format:
+ /// [从站地址][功能码][起始地址 (2)][读取数量 (2)]
+ ///
+ ///
///
- /// 写系统时间参数
- /// 写系统时间的核心
+ ///
+ /// 读时间输入结构 / Read Time Input Structure
+ ///
+ /// 包含从站地址、起始地址等参数
+ /// Contains slave address, start address, etc.
+ ///
+ ///
+ ///
+ /// 格式化后的字节数组 / Formatted Byte Array
+ ///
+ /// Modbus 读时间请求帧
+ /// Modbus read time request frame
+ ///
+ ///
public override byte[] Format(IInputStruct message)
{
var r_message = (GetSystemTimeModbusInputStruct)message;
@@ -126,11 +274,48 @@ namespace Modbus.Net.Modbus.SelfDefinedSample
}
///
- /// 反格式化
+ /// 反格式化 (接收后) / Unformat (After Receiving)
+ ///
+ /// 将响应字节数组解析为读时间输出结构
+ /// Parse response byte array to read time output structure
+ ///
+ /// 解析顺序 / Parse Order:
+ ///
+ /// - 从站地址 (1 字节) / Slave address (1 byte)
+ /// - 功能码 (1 字节) / Function code (1 byte)
+ /// - 字节数 (1 字节) / Byte count (1 byte)
+ /// - 年 (2 字节) / Year (2 bytes)
+ /// - 日 (1 字节) / Day (1 byte)
+ /// - 月 (1 字节) / Month (1 byte)
+ /// - 时 (2 字节) / Hour (2 bytes)
+ /// - 秒 (1 字节) / Second (1 byte)
+ /// - 分 (1 字节) / Minute (1 byte)
+ /// - 毫秒 (2 字节) / Millisecond (2 bytes)
+ ///
+ ///
+ ///
///
- /// 获取的信息
- /// 当前反格式化的位置
- /// 反格式化的信息
+ ///
+ /// 获取的信息 / Received Information
+ ///
+ /// 设备返回的字节数组
+ /// Byte array returned from device
+ ///
+ ///
+ ///
+ /// 当前反格式化的位置 / Current Unformat Position
+ ///
+ /// 引用传递,自动更新
+ /// Passed by reference, auto-updated
+ ///
+ ///
+ ///
+ /// 反格式化的信息 / Unformatted Information
+ ///
+ /// GetSystemTimeModbusOutputStruct 对象
+ /// GetSystemTimeModbusOutputStruct object
+ ///
+ ///
public override IOutputStruct Unformat(byte[] messageBytes, ref int flag)
{
var slaveAddress = ValueHelper.GetInstance(Endian).GetByte(messageBytes, ref flag);
@@ -150,25 +335,34 @@ namespace Modbus.Net.Modbus.SelfDefinedSample
#endregion
- #region 写PLC时间
+ #region 写 PLC 时间 / Write PLC Time
///
- /// 写时间输入
+ /// 写时间输入结构 / Write Time Input Structure
+ ///
+ /// 封装写入设备时间的请求参数
+ /// Encapsulates request parameters for writing device time
+ ///
///
public class SetSystemTimeModbusInputStruct : IInputStruct
{
///
- /// 构造函数
+ /// 构造函数 / Constructor
+ ///
+ /// 初始化写时间请求参数,从 DateTime 提取各时间分量
+ /// Initialize write time request parameters, extract time components from DateTime
+ ///
///
- /// 从站号
- /// 时间
+ /// 从站号 / Slave Address
+ /// 起始地址 / Start Address
+ /// 时间 / Time
public SetSystemTimeModbusInputStruct(byte slaveAddress, ushort startAddress, DateTime time)
{
SlaveAddress = slaveAddress;
FunctionCode = (byte)ModbusProtocolTimeFunctionCode.SetSystemTime;
StartAddress = startAddress;
- WriteCount = 5;
- WriteByteCount = 10;
+ WriteCount = 5; // 时间占用 5 个寄存器 / Time occupies 5 registers
+ WriteByteCount = 10; // 10 字节数据 / 10 bytes data
Year = (ushort)time.Year;
Day = (byte)time.Day;
Month = (byte)time.Month;
@@ -179,78 +373,86 @@ namespace Modbus.Net.Modbus.SelfDefinedSample
}
///
- /// 从站号
+ /// 从站号 / Slave Address
///
public byte SlaveAddress { get; }
///
- /// 功能码
+ /// 功能码 / Function Code
///
public byte FunctionCode { get; }
///
- /// 开始地址
+ /// 开始地址 / Start Address
///
public ushort StartAddress { get; }
///
- /// 写入个数
+ /// 写入个数 / Write Count
///
public ushort WriteCount { get; }
///
- /// 写入字节个数
+ /// 写入字节个数 / Write Byte Count
///
public byte WriteByteCount { get; }
///
- /// 年
+ /// 年 / Year
///
public ushort Year { get; }
///
- /// 日
+ /// 日 / Day
///
public byte Day { get; }
///
- /// 月
+ /// 月 / Month
///
public byte Month { get; }
///
- /// 时
+ /// 时 / Hour
///
public ushort Hour { get; }
///
- /// 秒
+ /// 秒 / Second
///
public byte Second { get; }
///
- /// 分
+ /// 分 / Minute
///
public byte Minute { get; }
///
- /// 毫秒
+ /// 毫秒 / Millisecond
///
public ushort Millisecond { get; }
}
///
- /// 写时间输出
+ /// 写时间输出结构 / Write Time Output Structure
+ ///
+ /// 封装写入设备时间的响应数据
+ /// Encapsulates response data for writing device time
+ ///
///
public class SetSystemTimeModbusOutputStruct : IOutputStruct
{
///
- /// 构造函数
+ /// 构造函数 / Constructor
+ ///
+ /// 初始化写时间响应数据
+ /// Initialize write time response data
+ ///
///
- /// 从站号
- /// 功能码
- /// 开始地址
- /// 写入个数
+ /// 从站号 / Slave Address
+ /// 功能码 / Function Code
+ /// 开始地址 / Start Address
+ /// 写入个数 / Write Count
public SetSystemTimeModbusOutputStruct(byte slaveAddress, byte functionCode,
ushort startAddress, ushort writeCount)
{
@@ -261,36 +463,60 @@ namespace Modbus.Net.Modbus.SelfDefinedSample
}
///
- /// 从站号
+ /// 从站号 / Slave Address
///
public byte SlaveAddress { get; private set; }
///
- /// 功能码
+ /// 功能码 / Function Code
///
public byte FunctionCode { get; private set; }
///
- /// 开始地址
+ /// 开始地址 / Start Address
///
public ushort StartAddress { get; private set; }
///
- /// 写入个数
+ /// 写入个数 / Write Count
///
public ushort WriteCount { get; private set; }
}
///
- /// 写系统时间协议
+ /// 写系统时间协议类 / Write System Time Protocol Class
+ ///
+ /// 实现写系统时间的协议格式化和反格式化
+ /// Implements protocol formatting and unformatting for writing system time
+ ///
///
public class SetSystemTimeModbusProtocol : ProtocolUnit
{
///
- /// 格式化
+ /// 格式化 (发送前) / Format (Before Sending)
+ ///
+ /// 将写时间请求参数转换为字节数组
+ /// Convert write time request parameters to byte array
+ ///
+ /// 格式 / Format:
+ /// [从站地址][功能码][起始地址 (2)][写入数量 (2)][字节数 (1)][年 (2)][日 (1)][月 (1)][时 (2)][秒 (1)][分 (1)][毫秒 (2)]
+ ///
+ ///
///
- /// 写系统时间的参数
- /// 写系统时间的核心
+ ///
+ /// 写时间输入结构 / Write Time Input Structure
+ ///
+ /// 包含从站地址、时间等参数
+ /// Contains slave address, time, etc.
+ ///
+ ///
+ ///
+ /// 格式化后的字节数组 / Formatted Byte Array
+ ///
+ /// Modbus 写时间请求帧
+ /// Modbus write time request frame
+ ///
+ ///
public override byte[] Format(IInputStruct message)
{
var r_message = (SetSystemTimeModbusInputStruct)message;
@@ -301,11 +527,39 @@ namespace Modbus.Net.Modbus.SelfDefinedSample
}
///
- /// 反格式化
+ /// 反格式化 (接收后) / Unformat (After Receiving)
+ ///
+ /// 将响应字节数组解析为写时间输出结构
+ /// Parse response byte array to write time output structure
+ ///
+ /// 解析顺序 / Parse Order:
+ ///
+ /// - 从站地址 (1 字节) / Slave address (1 byte)
+ /// - 功能码 (1 字节) / Function code (1 byte)
+ /// - 起始地址 (2 字节) / Start address (2 bytes)
+ /// - 写入数量 (2 字节) / Write count (2 bytes)
+ ///
+ ///
+ ///
///
- /// 获取的信息
- /// 当前反格式化的位置
- /// 反格式化的信息
+ ///
+ /// 获取的信息 / Received Information
+ ///
+ /// 设备返回的字节数组
+ /// Byte array returned from device
+ ///
+ ///
+ ///
+ /// 当前反格式化的位置 / Current Unformat Position
+ /// 引用传递,自动更新 / Passed by reference, auto-updated
+ ///
+ ///
+ /// 反格式化的信息 / Unformatted Information
+ ///
+ /// SetSystemTimeModbusOutputStruct 对象
+ /// SetSystemTimeModbusOutputStruct object
+ ///
+ ///
public override IOutputStruct Unformat(byte[] messageBytes, ref int flag)
{
var slaveAddress = ValueHelper.GetInstance(Endian).GetByte(messageBytes, ref flag);
@@ -317,4 +571,4 @@ namespace Modbus.Net.Modbus.SelfDefinedSample
}
#endregion
-}
\ No newline at end of file
+}
diff --git a/Modbus.Net/Modbus.Net.Modbus.SelfDefinedSample/ModbusUtilityTime.cs b/Modbus.Net/Modbus.Net.Modbus.SelfDefinedSample/ModbusUtilityTime.cs
index 63fca81..f23144c 100644
--- a/Modbus.Net/Modbus.Net.Modbus.SelfDefinedSample/ModbusUtilityTime.cs
+++ b/Modbus.Net/Modbus.Net.Modbus.SelfDefinedSample/ModbusUtilityTime.cs
@@ -1,20 +1,95 @@
-using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Logging;
using System;
using System.Threading.Tasks;
namespace Modbus.Net.Modbus.SelfDefinedSample
{
+ ///
+ /// Modbus 时间工具类 / Modbus Time Utility Class
+ ///
+ /// 提供 Modbus 设备时间读写功能,继承自 ModbusUtility
+ /// Provides Modbus device time read/write functionality, inherits from ModbusUtility
+ ///
+ /// 主要功能 / Main Functions:
+ ///
+ /// - GetTimeAsync - 读取 PLC 系统时间 / Read PLC system time
+ /// - SetTimeAsync - 设置 PLC 系统时间 / Set PLC system time
+ ///
+ ///
+ ///
+ /// 时间格式 / Time Format:
+ ///
+ /// - 年 (2 字节) / Year (2 bytes)
+ /// - 月 (1 字节) / Month (1 byte)
+ /// - 日 (1 字节) / Day (1 byte)
+ /// - 时 (2 字节) / Hour (2 bytes)
+ /// - 分 (1 字节) / Minute (1 byte)
+ /// - 秒 (1 字节) / Second (1 byte)
+ /// - 毫秒 (2 字节) / Millisecond (2 bytes)
+ ///
+ /// 共 10 字节,占用 5 个寄存器 / Total 10 bytes, occupies 5 registers
+ ///
+ ///
+ /// 使用示例 / Usage Example:
+ ///
+ /// // 创建时间工具实例 / Create time utility instance
+ /// var utility = new ModbusUtilityTime(
+ /// ModbusType.Tcp,
+ /// "192.168.1.100:502",
+ /// slaveAddress: 1,
+ /// masterAddress: 0,
+ /// endian: Endian.BigEndianLsb
+ /// );
+ ///
+ /// // 连接设备 / Connect to device
+ /// await utility.ConnectAsync();
+ ///
+ /// // 读取 PLC 时间 / Read PLC time
+ /// var timeResult = await utility.GetTimeAsync(startAddress: 0);
+ /// if (timeResult.IsSuccess)
+ /// {
+ /// Console.WriteLine($"PLC 时间:{timeResult.Datas}");
+ /// }
+ ///
+ /// // 同步 PLC 时间 / Sync PLC time
+ /// await utility.SetTimeAsync(startAddress: 0, DateTime.Now);
+ ///
+ ///
+ ///
+ ///
public class ModbusUtilityTime : ModbusUtility, IUtilityMethodTime
{
private static readonly ILogger logger = LogProvider.CreateLogger();
///
- /// 构造函数
+ /// 构造函数 (无连接字符串) / Constructor (without Connection String)
+ ///
+ /// 初始化 Modbus 时间工具,稍后通过 SetConnectionType 设置连接
+ /// Initialize Modbus time utility, set connection later via SetConnectionType
+ ///
///
- /// 协议类型
- /// 从站号
- /// 主站号
- /// 端格式
+ ///
+ /// 协议类型 / Protocol Type
+ ///
+ /// ModbusType 枚举值
+ /// ModbusType enum value
+ ///
+ ///
+ ///
+ /// 从站号 / Slave Address
+ /// Modbus 从站地址,范围 1-247 / Modbus slave address, range 1-247
+ ///
+ ///
+ /// 主站号 / Master Address
+ /// 通常为 0 或 1 / Usually 0 or 1
+ ///
+ ///
+ /// 端格式 / Endianness
+ ///
+ /// Modbus 标准使用 BigEndianLsb
+ /// Modbus standard uses BigEndianLsb
+ ///
+ ///
public ModbusUtilityTime(int connectionType, byte slaveAddress, byte masterAddress,
Endian endian)
: base(connectionType, slaveAddress, masterAddress, endian)
@@ -22,13 +97,26 @@ namespace Modbus.Net.Modbus.SelfDefinedSample
}
///
- /// 构造函数
+ /// 构造函数 (带连接字符串) / Constructor (with Connection String)
+ ///
+ /// 初始化 Modbus 时间工具并立即设置连接
+ /// Initialize Modbus time utility and set connection immediately
+ ///
///
- /// 协议类型
- /// 连接地址
- /// 从站号
- /// 主站号
- /// 端格式
+ ///
+ /// 协议类型 / Protocol Type
+ /// ModbusType 枚举值 / ModbusType enum value
+ ///
+ ///
+ /// 连接地址 / Connection Address
+ ///
+ /// TCP: "192.168.1.100:502"
+ /// 串口:"COM1" 或 "COM1,9600,None,8,1"
+ ///
+ ///
+ /// 从站号 / Slave Address
+ /// 主站号 / Master Address
+ /// 端格式 / Endianness
public ModbusUtilityTime(ModbusType connectionType, string connectionString, byte slaveAddress, byte masterAddress,
Endian endian)
: base(connectionType, connectionString, slaveAddress, masterAddress, endian)
@@ -36,17 +124,54 @@ namespace Modbus.Net.Modbus.SelfDefinedSample
}
///
- /// 读时间
+ /// 读时间 / Read Time
+ ///
+ /// 从 Modbus 设备读取当前系统时间
+ /// Read current system time from Modbus device
+ ///
+ /// 处理流程 / Processing Flow:
+ ///
+ /// - 创建 GetSystemTimeModbusInputStruct / Create GetSystemTimeModbusInputStruct
+ /// - 发送读时间请求 / Send read time request
+ /// - 接收 GetSystemTimeModbusOutputStruct / Receive GetSystemTimeModbusOutputStruct
+ /// - 解析时间并返回 / Parse time and return
+ /// - 处理异常情况 / Handle exceptions
+ ///
+ ///
+ ///
///
- /// 设备的时间
+ ///
+ /// 起始地址 / Start Address
+ ///
+ /// 时间寄存器起始地址
+ /// Starting address of time registers
+ ///
+ ///
+ ///
+ /// 设备的时间 / Device Time
+ ///
+ /// ReturnStruct<DateTime> 包含:
+ /// ReturnStruct<DateTime> contains:
+ ///
+ /// - Datas: 读取的时间值 / Read time value
+ /// - IsSuccess: 读取是否成功 / Read success flag
+ /// - ErrorCode: 错误码 / Error code
+ /// - ErrorMsg: 错误消息 / Error message
+ ///
+ ///
+ ///
public async Task> GetTimeAsync(ushort startAddress)
{
try
{
+ // 创建读时间输入结构 / Create read time input structure
var inputStruct = new GetSystemTimeModbusInputStruct(SlaveAddress, startAddress);
+
+ // 发送接收 / Send and receive
var outputStruct =
await Wrapper.SendReceiveAsync(
Wrapper[typeof(GetSystemTimeModbusProtocol)], inputStruct);
+
return new ReturnStruct
{
Datas = outputStruct?.Time ?? DateTime.MinValue,
@@ -69,18 +194,62 @@ namespace Modbus.Net.Modbus.SelfDefinedSample
}
///
- /// 写时间
+ /// 写时间 / Write Time
+ ///
+ /// 向 Modbus 设备写入系统时间
+ /// Write system time to Modbus device
+ ///
+ /// 处理流程 / Processing Flow:
+ ///
+ /// - 创建 SetSystemTimeModbusInputStruct / Create SetSystemTimeModbusInputStruct
+ /// - 从 DateTime 提取时间分量 / Extract time components from DateTime
+ /// - 发送写时间请求 / Send write time request
+ /// - 接收 SetSystemTimeModbusOutputStruct / Receive SetSystemTimeModbusOutputStruct
+ /// - 检查写入结果 / Check write result
+ /// - 处理异常情况 / Handle exceptions
+ ///
+ ///
+ ///
///
- /// 需要写入的时间
- /// 写入是否成功
+ ///
+ /// 起始地址 / Start Address
+ ///
+ /// 时间寄存器起始地址
+ /// Starting address of time registers
+ ///
+ ///
+ ///
+ /// 需要写入的时间 / Time to Write
+ ///
+ /// DateTime 对象,包含年月日时分秒毫秒
+ /// DateTime object with year, month, day, hour, minute, second, millisecond
+ ///
+ ///
+ ///
+ /// 写入是否成功 / Whether Write is Successful
+ ///
+ /// ReturnStruct<bool>:
+ ///
+ /// - Datas: true=成功,false=失败 / true=success, false=failure
+ /// - IsSuccess: 操作是否成功 / Operation success flag
+ /// - ErrorCode: 错误码 / Error code
+ /// - ErrorMsg: 错误消息 / Error message
+ ///
+ ///
+ ///
public async Task> SetTimeAsync(ushort startAddress, DateTime setTime)
{
try
{
+ // 创建写时间输入结构 / Create write time input structure
var inputStruct = new SetSystemTimeModbusInputStruct(SlaveAddress, startAddress, setTime);
+
+ // 发送接收 / Send and receive
var outputStruct =
await Wrapper.SendReceiveAsync(
Wrapper[typeof(SetSystemTimeModbusProtocol)], inputStruct);
+
+ // 检查写入结果 / Check write result
return new ReturnStruct()
{
Datas = outputStruct?.WriteCount > 0,
diff --git a/Modbus.Net/Modbus.Net.Modbus/AddressFormaterModbus.cs b/Modbus.Net/Modbus.Net.Modbus/AddressFormaterModbus.cs
index 5b21c40..1058a6c 100644
--- a/Modbus.Net/Modbus.Net.Modbus/AddressFormaterModbus.cs
+++ b/Modbus.Net/Modbus.Net.Modbus/AddressFormaterModbus.cs
@@ -1,31 +1,178 @@
-namespace Modbus.Net.Modbus
+namespace Modbus.Net.Modbus
{
///
- /// Modbus标准AddressFormater
+ /// Modbus 标准地址格式化器 / Modbus Standard Address Formater
+ ///
+ /// 实现 Modbus 协议的标准地址格式化功能
+ /// Implements standard address formatting functionality for Modbus protocol
+ ///
+ /// 地址格式 / Address Format:
+ ///
+ /// - 无子地址: "Area Address" (如 "4X 1") / Without sub-address: "Area Address" (e.g., "4X 1")
+ /// - 有子地址: "Area Address.SubAddress" (如 "4X 1.3") / With sub-address: "Area Address.SubAddress" (e.g., "4X 1.3")
+ ///
+ ///
+ ///
+ /// 地址组成 / Address Components:
+ ///
+ /// - Area - 区域标识 (0X/1X/3X/4X) / Area identifier (0X/1X/3X/4X)
+ /// - Address - 地址偏移 / Address offset
+ /// - SubAddress - 子地址 (位偏移 0-7) / Sub-address (bit offset 0-7)
+ ///
+ ///
+ ///
+ /// 区域说明 / Area Description:
+ ///
+ /// - 0X - 线圈 (Coil) - 可读可写 / Readable and Writable
+ /// - 1X - 离散输入 (Discrete Input) - 只读 / Read-only
+ /// - 3X - 输入寄存器 (Input Register) - 只读 / Read-only
+ /// - 4X - 保持寄存器 (Holding Register) - 可读可写 / Readable and Writable
+ ///
+ ///
+ ///
+ /// 使用示例 / Usage Example:
+ ///
+ /// var formater = new AddressFormaterModbus();
+ ///
+ /// // 格式化地址 (无子地址) / Format address (without sub-address)
+ /// string addr1 = formater.FormatAddress("4X", 1);
+ /// // 结果:"4X 1"
+ ///
+ /// // 格式化地址 (有子地址) / Format address (with sub-address)
+ /// string addr2 = formater.FormatAddress("4X", 5, 3);
+ /// // 结果:"4X 5.3" (保持寄存器第 5 个的第 3 位)
+ ///
+ ///
+ ///
///
public class AddressFormaterModbus : AddressFormater
{
///
- /// 格式化地址
+ /// 格式化地址 (无子地址) / Format Address (without Sub-Address)
+ ///
+ /// 将区域和地址转换为 Modbus 标准格式字符串
+ /// Convert area and address to Modbus standard format string
+ ///
+ /// 格式 / Format:
+ /// Area + " " + Address
+ ///
+ ///
+ /// 示例 / Examples:
+ ///
+ /// - Area="4X", Address=1 → "4X 1"
+ /// - Area="0X", Address=10 → "0X 10"
+ /// - Area="3X", Address=5 → "3X 5"
+ ///
+ ///
+ ///
///
- /// 地址区域
- /// 地址
- /// 格式化的地址字符串
+ ///
+ /// 地址区域 / Address Area
+ ///
+ /// Modbus 区域标识
+ /// Modbus area identifier
+ ///
+ /// 有效值 / Valid Values:
+ ///
+ /// - "0X" - 线圈 / Coil
+ /// - "1X" - 离散输入 / Discrete Input
+ /// - "3X" - 输入寄存器 / Input Register
+ /// - "4X" - 保持寄存器 / Holding Register
+ ///
+ ///
+ ///
+ ///
+ ///
+ /// 地址 / Address
+ ///
+ /// 地址偏移量 (从 1 开始)
+ /// Address offset (starts from 1)
+ ///
+ /// 示例 / Examples:
+ ///
+ /// - 40001 → Address=1
+ /// - 40002 → Address=2
+ ///
+ ///
+ ///
+ ///
+ ///
+ /// 格式化的地址字符串 / Formatted Address String
+ ///
+ /// 格式:"Area Address"
+ /// Format: "Area Address"
+ ///
+ ///
public override string FormatAddress(string area, int address)
{
return area + " " + address;
}
///
- /// 格式化地址
+ /// 格式化地址 (带子地址) / Format Address (with Sub-Address)
+ ///
+ /// 将区域、地址和子地址转换为 Modbus 标准格式字符串
+ /// Convert area, address and sub-address to Modbus standard format string
+ ///
+ /// 格式 / Format:
+ /// Area + " " + Address + "." + SubAddress
+ ///
+ ///
+ /// 示例 / Examples:
+ ///
+ /// - Area="4X", Address=1, SubAddress=0 → "4X 1.0"
+ /// - Area="0X", Address=10, SubAddress=3 → "0X 10.3" (线圈第 10 个的第 3 位)
+ /// - Area="4X", Address=5, SubAddress=7 → "4X 5.7" (保持寄存器第 5 个的第 7 位)
+ ///
+ ///
+ ///
+ /// 子地址说明 / Sub-Address Description:
+ ///
+ /// - 范围:0-7 (一个字节的 8 个位) / Range: 0-7 (8 bits of one byte)
+ /// - 用于访问单个位 / Used to access individual bits
+ /// - 仅对 Boolean 类型有效 / Only valid for Boolean type
+ ///
+ ///
+ ///
///
- /// 地址区域
- /// 地址
- /// 比特位地址
- /// 格式化的地址字符串
+ ///
+ /// 地址区域 / Address Area
+ ///
+ /// Modbus 区域标识
+ /// Modbus area identifier
+ ///
+ ///
+ ///
+ /// 地址 / Address
+ ///
+ /// 地址偏移量
+ /// Address offset
+ ///
+ ///
+ ///
+ /// 比特位地址 / Bit Address
+ ///
+ /// 子地址 (位偏移)
+ /// Sub-address (bit offset)
+ ///
+ /// 范围 / Range: 0-7
+ ///
+ /// - 0 - 最低位 / LSB
+ /// - 7 - 最高位 / MSB
+ ///
+ ///
+ ///
+ ///
+ ///
+ /// 格式化的地址字符串 / Formatted Address String
+ ///
+ /// 格式:"Area Address.SubAddress"
+ /// Format: "Area Address.SubAddress"
+ ///
+ ///
public override string FormatAddress(string area, int address, int subAddress)
{
return area + " " + address + "." + subAddress;
}
}
-}
\ No newline at end of file
+}
diff --git a/Modbus.Net/Modbus.Net.Modbus/AddressTranslatorModbus.cs b/Modbus.Net/Modbus.Net.Modbus/AddressTranslatorModbus.cs
index bfd0f2f..007a589 100644
--- a/Modbus.Net/Modbus.Net.Modbus/AddressTranslatorModbus.cs
+++ b/Modbus.Net/Modbus.Net.Modbus/AddressTranslatorModbus.cs
@@ -1,27 +1,110 @@
-using System.Collections.Generic;
+using System.Collections.Generic;
namespace Modbus.Net.Modbus
{
///
- /// Modbus地址翻译器基类
+ /// Modbus 地址翻译器基类 / Modbus Address Translator Base Class
+ ///
+ /// 提供 Modbus 地址翻译的基础功能,支持读/写操作
+ /// Provides base functionality for Modbus address translation, supporting read/write operations
+ ///
+ /// 主要功能 / Main Functions:
+ ///
+ /// - 将用户友好的地址字符串转换为内部地址结构 / Convert user-friendly address strings to internal address structure
+ /// - 根据区域 (0X/1X/3X/4X) 确定功能码 / Determine function code based on area (0X/1X/3X/4X)
+ /// - 处理子地址 (位偏移) / Handle sub-address (bit offset)
+ /// - 计算区域字节宽度 / Calculate area byte width
+ ///
+ ///
+ ///
+ /// 地址格式 / Address Format:
+ /// "Area Address[.SubAddress]"
+ ///
+ /// - "4X 1" - 保持寄存器第 1 个 / Holding Register #1
+ /// - "0X 10" - 线圈第 10 个 / Coil #10
+ /// - "4X 5.3" - 保持寄存器第 5 个的第 3 位 / Bit 3 of Holding Register #5
+ ///
+ ///
+ ///
///
public abstract class ModbusTranslatorBase : AddressTranslator
{
///
- /// 地址转换
+ /// 地址转换 (支持 isSingle 参数) / Address Translate (with isSingle parameter)
+ ///
+ /// 将格式化的地址字符串翻译为 AddressDef 对象
+ /// Translate formatted address string to AddressDef object
+ ///
+ /// 参数说明 / Parameters:
+ ///
+ /// - address - 格式化的地址字符串 / Formatted address string
+ /// - isRead - 是否为读取操作 / Whether it's a read operation
+ /// - isSingle - 是否只写入一个数据 / Whether writing single data only
+ ///
+ ///
+ ///
+ /// isSingle 参数用途 / isSingle Parameter Purpose:
+ ///
+ /// - true: 使用单写功能码 (05/06) / Use single write function code (05/06)
+ /// - false: 使用多写功能码 (15/16) / Use multi-write function code (15/16)
+ ///
+ ///
+ ///
///
- /// 格式化的地址
- /// 是否为读取,是为读取,否为写入
- /// 是否只写入一个数据
- /// 翻译后的地址
+ ///
+ /// 格式化的地址 / Formatted Address
+ ///
+ /// 示例 / Examples:
+ ///
+ /// - "4X 1" - 保持寄存器 / Holding Register
+ /// - "0X 10" - 线圈 / Coil
+ /// - "3X 5" - 输入寄存器 / Input Register
+ /// - "1X 20" - 离散输入 / Discrete Input
+ ///
+ ///
+ ///
+ ///
+ /// 是否为读取 / Whether it's Read
+ ///
+ /// true: 读取操作 / Read operation
+ /// false: 写入操作 / Write operation
+ ///
+ ///
+ ///
+ /// 是否只写入一个数据 / Whether Writing Single Data
+ ///
+ /// true: 单个写入 / Single write
+ /// false: 多个写入 / Multi write
+ ///
+ /// 仅写入操作时有效 / Only valid for write operations
+ ///
+ ///
+ ///
+ ///
+ /// 翻译后的地址 / Translated Address
+ ///
+ /// AddressDef 包含:
+ /// AddressDef contains:
+ ///
+ /// - AreaString: 区域字符串 (如 "4X") / Area string (e.g., "4X")
+ /// - Area: 功能码 / Function code
+ /// - Address: 内部地址 (从 0 开始) / Internal address (starts from 0)
+ /// - SubAddress: 子地址 (位偏移) / Sub-address (bit offset)
+ ///
+ ///
+ ///
public abstract AddressDef AddressTranslate(string address, bool isRead, bool isSingle);
///
- /// 地址转换
+ /// 地址转换 (默认 isSingle=false) / Address Translate (default isSingle=false)
+ ///
+ /// 重载方法,默认使用多写模式
+ /// Overloaded method, defaults to multi-write mode
+ ///
///
- /// 格式化的地址
- /// 是否为读取,是为读取,否为写入
- /// 翻译后的地址
+ /// 格式化的地址 / Formatted Address
+ /// 是否为读取 / Whether it's Read
+ /// 翻译后的地址 / Translated Address
public override AddressDef AddressTranslate(string address, bool isRead)
{
return AddressTranslate(address, isRead, false);
@@ -29,83 +112,167 @@ namespace Modbus.Net.Modbus
}
///
- /// Modbus数据单元翻译器
+ /// Modbus 数据单元翻译器 / Modbus Data Unit Translator
+ ///
+ /// 实现 Modbus 协议的地址翻译功能,支持所有标准 Modbus 区域
+ /// Implements Modbus protocol address translation functionality, supporting all standard Modbus areas
+ ///
+ /// 支持的区域 / Supported Areas:
+ ///
+ /// - 0X - 线圈 (Coil) - 可读可写 / Readable and Writable
+ /// - 1X - 离散输入 (Discrete Input) - 只读 / Read-only
+ /// - 3X - 输入寄存器 (Input Register) - 只读 / Read-only
+ /// - 4X - 保持寄存器 (Holding Register) - 可读可写 / Readable and Writable
+ ///
+ ///
+ ///
+ /// 功能码映射 / Function Code Mapping:
+ ///
+ /// - 读取 0X: 功能码 01 (Read Coil Status)
+ /// - 读取 1X: 功能码 02 (Read Input Status)
+ /// - 读取 3X: 功能码 04 (Read Input Register)
+ /// - 读取 4X: 功能码 03 (Read Holding Register)
+ /// - 单写 0X: 功能码 05 (Write Single Coil)
+ /// - 单写 4X: 功能码 06 (Write Single Register)
+ /// - 多写 0X: 功能码 15 (Write Multi Coil)
+ /// - 多写 4X: 功能码 16 (Write Multi Register)
+ ///
+ ///
+ ///
+ /// 使用示例 / Usage Example:
+ ///
+ /// var translator = new AddressTranslatorModbus();
+ ///
+ /// // 翻译读取请求 / Translate read request
+ /// AddressDef readAddr = translator.AddressTranslate("4X 1", isRead: true);
+ /// // 结果:AreaString="4X", Area=3, Address=0, SubAddress=0
+ ///
+ /// // 翻译写入请求 (单个) / Translate write request (single)
+ /// AddressDef writeSingleAddr = translator.AddressTranslate("4X 1", isRead: false, isSingle: true);
+ /// // 结果:AreaString="4X", Area=6, Address=0, SubAddress=0
+ ///
+ /// // 翻译写入请求 (多个) / Translate write request (multi)
+ /// AddressDef writeMultiAddr = translator.AddressTranslate("4X 1", isRead: false, isSingle: false);
+ /// // 结果:AreaString="4X", Area=16, Address=0, SubAddress=0
+ ///
+ /// // 获取区域字节宽度 / Get area byte width
+ /// double byteWidth = translator.GetAreaByteLength("4X"); // 返回:2.0
+ ///
+ ///
+ ///
///
public class AddressTranslatorModbus : ModbusTranslatorBase
{
///
- /// 读功能码
+ /// 读功能码字典 / Read Function Code Dictionary
+ ///
+ /// 存储各区域的读功能码和字节宽度
+ /// Stores read function codes and byte widths for each area
+ ///
+ /// 数据结构 / Data Structure:
+ ///
+ /// - Key: 区域字符串 (如 "0X", "4X") / Area string (e.g., "0X", "4X")
+ /// - Value: AreaOutputDef (功能码 + 字节宽度) / AreaOutputDef (function code + byte width)
+ ///
+ ///
+ ///
///
protected Dictionary ReadFunctionCodeDictionary;
///
- /// 写功能码
+ /// 写功能码字典 / Write Function Code Dictionary
+ ///
+ /// 存储各区域的写功能码和字节宽度,区分单写和多写
+ /// Stores write function codes and byte widths for each area, distinguishing single and multi-write
+ ///
+ /// 数据结构 / Data Structure:
+ ///
+ /// - Key: (区域字符串,是否单写) / (Area string, is single write)
+ /// - Value: AreaOutputDef (功能码 + 字节宽度) / AreaOutputDef (function code + byte width)
+ ///
+ ///
+ ///
///
protected Dictionary<(string, bool), AreaOutputDef> WriteFunctionCodeDictionary;
///
- /// 构造器
+ /// 构造函数 / Constructor
+ ///
+ /// 初始化读/写功能码字典
+ /// Initialize read/write function code dictionaries
+ ///
///
public AddressTranslatorModbus()
{
+ // 初始化读功能码字典 / Initialize read function code dictionary
ReadFunctionCodeDictionary = new Dictionary
{
{
- "0X",
+ "0X", // 线圈 / Coil
new AreaOutputDef
{
- Code = (int) ModbusProtocolFunctionCode.ReadCoilStatus,
- AreaWidth = 0.125
+ Code = (int)ModbusProtocolFunctionCode.ReadCoilStatus, // 功能码 01
+ AreaWidth = 0.125 // 1 位 = 0.125 字节
}
},
{
- "1X",
+ "1X", // 离散输入 / Discrete Input
new AreaOutputDef
{
- Code = (int) ModbusProtocolFunctionCode.ReadInputStatus,
- AreaWidth = 0.125
+ Code = (int)ModbusProtocolFunctionCode.ReadInputStatus, // 功能码 02
+ AreaWidth = 0.125 // 1 位 = 0.125 字节
}
},
{
- "3X",
- new AreaOutputDef {Code = (int) ModbusProtocolFunctionCode.ReadInputRegister, AreaWidth = 2}
+ "3X", // 输入寄存器 / Input Register
+ new AreaOutputDef
+ {
+ Code = (int)ModbusProtocolFunctionCode.ReadInputRegister, // 功能码 04
+ AreaWidth = 2 // 16 位 = 2 字节
+ }
},
{
- "4X",
- new AreaOutputDef {Code = (int) ModbusProtocolFunctionCode.ReadHoldRegister, AreaWidth = 2}
+ "4X", // 保持寄存器 / Holding Register
+ new AreaOutputDef
+ {
+ Code = (int)ModbusProtocolFunctionCode.ReadHoldRegister, // 功能码 03
+ AreaWidth = 2 // 16 位 = 2 字节
+ }
}
};
+
+ // 初始化写功能码字典 / Initialize write function code dictionary
WriteFunctionCodeDictionary = new Dictionary<(string, bool), AreaOutputDef>
{
{
- ("0X", false),
+ ("0X", false), // 多写线圈 / Multi-write Coil
new AreaOutputDef
{
- Code = (int) ModbusProtocolFunctionCode.WriteMultiCoil,
+ Code = (int)ModbusProtocolFunctionCode.WriteMultiCoil, // 功能码 15
AreaWidth = 0.125
}
},
{
- ("4X", false),
+ ("4X", false), // 多写寄存器 / Multi-write Register
new AreaOutputDef
{
- Code = (int) ModbusProtocolFunctionCode.WriteMultiRegister,
+ Code = (int)ModbusProtocolFunctionCode.WriteMultiRegister, // 功能码 16
AreaWidth = 2
}
},
{
- ("0X", true),
+ ("0X", true), // 单写线圈 / Single-write Coil
new AreaOutputDef
{
- Code = (int) ModbusProtocolFunctionCode.WriteSingleCoil,
+ Code = (int)ModbusProtocolFunctionCode.WriteSingleCoil, // 功能码 05
AreaWidth = 0.125
}
},
{
- ("4X", true),
+ ("4X", true), // 单写寄存器 / Single-write Register
new AreaOutputDef
{
- Code = (int) ModbusProtocolFunctionCode.WriteSingleRegister,
+ Code = (int)ModbusProtocolFunctionCode.WriteSingleRegister, // 功能码 06
AreaWidth = 2
}
}
@@ -113,35 +280,98 @@ namespace Modbus.Net.Modbus
}
///
- /// 地址转换
+ /// 地址转换 / Address Translate
+ ///
+ /// 将格式化的地址字符串翻译为 AddressDef 对象
+ /// Translate formatted address string to AddressDef object
+ ///
+ /// 处理流程 / Processing Flow:
+ ///
+ /// - 将地址转为大写 / Convert address to uppercase
+ /// - 按空格分割地址字符串 / Split address string by space
+ /// - 提取区域 (head) 和地址 (tail) / Extract area (head) and address (tail)
+ /// - 处理子地址 (如果有"."分隔) / Handle sub-address (if "." separator exists)
+ /// - 根据读/写选择功能码字典 / Select function code dictionary based on read/write
+ /// - 创建并返回 AddressDef / Create and return AddressDef
+ ///
+ ///
+ ///
+ /// 地址转换规则 / Address Translation Rules:
+ ///
+ /// - Modbus 地址从 1 开始,内部地址从 0 开始 / Modbus addresses start from 1, internal addresses start from 0
+ /// - 例如:"4X 1" → Address=0, "4X 100" → Address=99
+ ///
+ ///
+ ///
///
- /// 格式化的地址
- /// 是否为读取,是为读取,否为写入
- /// 是否只写入一个数据
- /// 翻译后的地址
+ ///
+ /// 格式化的地址 / Formatted Address
+ ///
+ /// 示例 / Examples:
+ ///
+ /// - "4X 1" - 保持寄存器第 1 个
+ /// - "0X 10" - 线圈第 10 个
+ /// - "4X 5.3" - 保持寄存器第 5 个的第 3 位
+ ///
+ ///
+ ///
+ ///
+ /// 是否为读取 / Whether it's Read
+ ///
+ /// true: 读取操作 / Read operation
+ /// false: 写入操作 / Write operation
+ ///
+ ///
+ ///
+ /// 是否只写入一个数据 / Whether Writing Single Data
+ ///
+ /// true: 单个写入 / Single write
+ /// false: 多个写入 / Multi write
+ ///
+ ///
+ ///
+ /// 翻译后的地址 / Translated Address
+ ///
+ /// AddressDef 包含:
+ /// AddressDef contains:
+ ///
+ /// - AreaString: 区域字符串 (如 "4X") / Area string
+ /// - Area: 功能码 / Function code
+ /// - Address: 内部地址 (从 0 开始) / Internal address (starts from 0)
+ /// - SubAddress: 子地址 (位偏移) / Sub-address (bit offset)
+ ///
+ ///
+ ///
public override AddressDef AddressTranslate(string address, bool isRead, bool isSingle)
{
+ // 转为大写 / Convert to uppercase
address = address.ToUpper();
+
+ // 按空格分割 / Split by space
var splitString = address.Split(' ');
- var head = splitString[0];
- var tail = splitString[1];
+ var head = splitString[0]; // 区域 / Area (e.g., "4X")
+ var tail = splitString[1]; // 地址 / Address (e.g., "1" or "5.3")
+
+ // 处理子地址 / Handle sub-address
string sub;
if (tail.Contains("."))
{
var splitString2 = tail.Split('.');
- sub = splitString2[1];
- tail = splitString2[0];
+ sub = splitString2[1]; // 子地址 / Sub-address
+ tail = splitString2[0]; // 主地址 / Main address
}
else
{
- sub = "0";
+ sub = "0"; // 默认子地址为 0 / Default sub-address is 0
}
+
+ // 根据读/写选择功能码 / Select function code based on read/write
return isRead
? new AddressDef
{
AreaString = head,
Area = ReadFunctionCodeDictionary[head].Code,
- Address = int.Parse(tail) - 1,
+ Address = int.Parse(tail) - 1, // Modbus 地址从 1 开始,内部从 0 开始
SubAddress = int.Parse(sub)
}
: new AddressDef
@@ -154,13 +384,49 @@ namespace Modbus.Net.Modbus
}
///
- /// 获取区域中的单个地址占用的字节长度
+ /// 获取区域中的单个地址占用的字节长度 / Get Byte Length per Address in Area
+ ///
+ /// 从读功能码字典中获取区域的字节宽度
+ /// Get byte width of area from read function code dictionary
+ ///
+ /// 返回值说明 / Return Value Description:
+ ///
+ /// - 0X/1X (线圈/离散输入): 0.125 字节 (1 位) / Coils/Discrete Inputs: 0.125 bytes (1 bit)
+ /// - 3X/4X (寄存器): 2 字节 (16 位) / Registers: 2 bytes (16 bits)
+ ///
+ ///
+ ///
+ /// 使用场景 / Use Cases:
+ ///
+ /// - 地址组合器计算地址跨度 / Address combiner calculates address span
+ /// - 计算需要读取的字节数 / Calculate number of bytes to read
+ /// - 优化数据打包 / Optimize data packing
+ ///
+ ///
+ ///
///
- /// 区域名称
- /// 字节长度
+ ///
+ /// 区域名称 / Area Name
+ ///
+ /// 示例 / Examples:
+ ///
+ /// - "0X" - 线圈 / Coil
+ /// - "4X" - 保持寄存器 / Holding Register
+ ///
+ ///
+ ///
+ ///
+ /// 字节长度 / Byte Length
+ ///
+ ///
+ /// - 0X/1X: 0.125 字节
+ /// - 3X/4X: 2 字节
+ ///
+ ///
+ ///
public override double GetAreaByteLength(string area)
{
return ReadFunctionCodeDictionary[area].AreaWidth;
}
}
-}
\ No newline at end of file
+}
diff --git a/Modbus.Net/Modbus.Net.Modbus/Interfaces.cs b/Modbus.Net/Modbus.Net.Modbus/Interfaces.cs
index 1ef750d..1308bff 100644
--- a/Modbus.Net/Modbus.Net.Modbus/Interfaces.cs
+++ b/Modbus.Net/Modbus.Net.Modbus/Interfaces.cs
@@ -1,219 +1,649 @@
-using System.Threading.Tasks;
+using System.Threading.Tasks;
namespace Modbus.Net.Modbus
{
///
- /// 异常状态获取方法
+ /// Modbus 高级功能接口定义 / Modbus Advanced Function Interface Definitions
+ ///
+ /// 定义 Modbus 协议的高级功能接口,包括诊断、事件日志、文件记录等
+ /// Defines advanced function interfaces for Modbus protocol, including diagnostics, event log, file record, etc.
+ ///
+ /// 主要接口 / Main Interfaces:
+ ///
+ /// - IUtilityMethodExceptionStatus - 异常状态获取 / Exception status get
+ /// - IUtilityMethodDiagnotics - 诊断功能 / Diagnostics
+ /// - IUtilityMethodCommEventCounter - 通讯事件计数器 / Comm event counter
+ /// - IUtilityMethodCommEventLog - 通讯事件日志 / Comm event log
+ /// - IUtilityMethodSlaveId - 从站 ID 获取 / Slave ID get
+ /// - IUtilityMethodFileRecord - 文件记录读写 / File record read/write
+ /// - IUtilityMethodMaskRegister - 掩码写寄存器 / Mask write register
+ /// - IUtilityMethodMultipleRegister - 读写多寄存器 / Read/write multiple registers
+ /// - IUtilityMethodFIFOQueue - FIFO 队列读 / FIFO queue read
+ ///
+ ///
+ ///
+ /// 使用场景 / Use Cases:
+ ///
+ /// - 设备诊断和维护 / Device diagnostics and maintenance
+ /// - 通讯状态监控 / Communication status monitoring
+ /// - 高级数据操作 / Advanced data manipulation
+ ///
+ ///
+ ///
+ ///
+
+ #region 异常状态获取 / Exception Status Get
+
+ ///
+ /// 异常状态获取方法接口 / Exception Status Get Method Interface
+ ///
+ /// 实现 Modbus 功能码 07 (Read Exception Status)
+ /// Implements Modbus function code 07 (Read Exception Status)
+ ///
+ /// 使用场景 / Use Cases:
+ ///
+ /// - 检查从站设备异常状态 / Check slave device exception status
+ /// - 仅用于串行通信 (RTU/ASCII) / Serial communication only (RTU/ASCII)
+ ///
+ ///
+ ///
///
public interface IUtilityMethodExceptionStatus
{
///
- /// 获取异常状态
+ /// 获取异常状态 / Get Exception Status
+ ///
+ /// 读取从站设备的异常状态字节
+ /// Read exception status byte from slave device
+ ///
+ /// 异常状态字节含义 / Exception Status Byte Meaning:
+ ///
+ /// - Bit 0: 无效数据 / Invalid data
+ /// - Bit 1: 从站配置更改 / Slave configuration change
+ /// - Bit 2: 功能码不支持 / Function code not supported
+ /// - Bit 3: 从站故障 / Slave failure
+ /// - Bit 4-7: 保留 / Reserved
+ ///
+ ///
+ ///
///
- ///
+ ///
+ /// 异常状态字节 / Exception Status Byte
+ ///
+ /// ReturnStruct<byte> 包含:
+ /// ReturnStruct<byte> contains:
+ ///
+ /// - Datas: 异常状态字节 / Exception status byte
+ /// - IsSuccess: 读取是否成功 / Read success flag
+ /// - ErrorCode: 错误码 / Error code
+ /// - ErrorMsg: 错误消息 / Error message
+ ///
+ ///
+ ///
Task> GetExceptionStatusAsync();
}
+ #endregion
+
+ #region 诊断数据 / Diagnostics Data
+
///
- /// 诊断返回数据
+ /// 诊断返回数据类 / Diagnostics Return Data Class
+ ///
+ /// 存储 Modbus 诊断功能的返回数据
+ /// Stores return data for Modbus diagnostics functionality
+ ///
///
public class DiagnoticsData
{
///
- /// 子方法编号
+ /// 子方法编号 / Sub-function Number
+ ///
+ /// 诊断功能的子功能码
+ /// Sub-function code for diagnostics
+ ///
+ /// 常见子功能 / Common Sub-functions:
+ ///
+ /// - 0000: 查询从站 / Query slave
+ /// - 0001: 重启通信 / Restart communications
+ /// - 0002: 返回诊断寄存器 / Return diagnostic register
+ ///
+ ///
+ ///
///
public ushort SubFunction { get; set; }
///
- /// 诊断数据
+ /// 诊断数据 / Diagnostics Data
+ ///
+ /// 诊断功能返回的数据数组
+ /// Data array returned by diagnostics function
+ ///
///
public ushort[] Data { get; set; }
}
///
- /// 诊断获取方法
+ /// 诊断获取方法接口 / Diagnostics Get Method Interface
+ ///
+ /// 实现 Modbus 功能码 08 (Diagnostics)
+ /// Implements Modbus function code 08 (Diagnostics)
+ ///
+ /// 使用场景 / Use Cases:
+ ///
+ /// - 设备诊断和测试 / Device diagnostics and testing
+ /// - 仅用于串行通信 (RTU/ASCII) / Serial communication only (RTU/ASCII)
+ ///
+ ///
+ ///
///
public interface IUtilityMethodDiagnotics
{
///
- /// 获取诊断信息
+ /// 获取诊断信息 / Get Diagnostics Information
+ ///
+ /// 执行 Modbus 诊断功能
+ /// Execute Modbus diagnostics function
+ ///
///
- /// 子方法编号
- /// 诊断数据
- ///
+ ///
+ /// 子方法编号 / Sub-function Number
+ ///
+ /// 诊断功能的子功能码
+ /// Sub-function code for diagnostics
+ ///
+ ///
+ ///
+ /// 诊断数据 / Diagnostics Data
+ ///
+ /// 传递给诊断功能的数据
+ /// Data passed to diagnostics function
+ ///
+ ///
+ ///
+ /// 诊断数据 / Diagnostics Data
+ ///
+ /// ReturnStruct<DiagnoticsData> 包含诊断结果
+ /// ReturnStruct<DiagnoticsData> contains diagnostics result
+ ///
+ ///
Task> GetDiagnoticsAsync(ushort subFunction, ushort[] data);
}
+ #endregion
+
+ #region 通讯事件计数器 / Comm Event Counter
+
///
- /// 通讯事件计数器获取数据
+ /// 通讯事件计数器数据类 / Comm Event Counter Data Class
+ ///
+ /// 存储 Modbus 通讯事件计数器的数据
+ /// Stores data for Modbus comm event counter
+ ///
///
public class CommEventCounterData
{
///
- /// 通讯状态
+ /// 通讯状态 / Communication Status
+ ///
+ /// 从站设备的通讯状态字
+ /// Communication status word of slave device
+ ///
///
public ushort Status { get; set; }
///
- /// 事件计数
+ /// 事件计数 / Event Count
+ ///
+ /// 通讯事件的数量
+ /// Number of communication events
+ ///
///
public ushort EventCount { get; set; }
}
///
- /// 通讯事件计数器获取方法
+ /// 通讯事件计数器获取方法接口 / Comm Event Counter Get Method Interface
+ ///
+ /// 实现 Modbus 功能码 11 (Get Comm Event Counter)
+ /// Implements Modbus function code 11 (Get Comm Event Counter)
+ ///
+ /// 使用场景 / Use Cases:
+ ///
+ /// - 监控通讯事件数量 / Monitor communication event count
+ /// - 仅用于串行通信 (RTU/ASCII) / Serial communication only (RTU/ASCII)
+ ///
+ ///
+ ///
///
public interface IUtilityMethodCommEventCounter
{
///
- /// 获取通讯事件计数器
+ /// 获取通讯事件计数器 / Get Comm Event Counter
+ ///
+ /// 读取从站设备的通讯事件计数器
+ /// Read comm event counter from slave device
+ ///
///
- ///
+ ///
+ /// 通讯事件计数器数据 / Comm Event Counter Data
+ ///
+ /// ReturnStruct<CommEventCounterData> 包含计数结果
+ /// ReturnStruct<CommEventCounterData> contains counter result
+ ///
+ ///
Task> GetCommEventCounterAsync();
}
+ #endregion
+
+ #region 通讯事件日志 / Comm Event Log
+
///
- /// 通讯事件获取数据
+ /// 通讯事件数据类 / Comm Event Data Class
+ ///
+ /// 存储 Modbus 通讯事件日志的数据
+ /// Stores data for Modbus comm event log
+ ///
///
public class CommEventLogData
{
///
- /// 状态
+ /// 状态 / Status
+ ///
+ /// 从站设备的状态字
+ /// Status word of slave device
+ ///
///
public ushort Status { get; set; }
///
- /// 事件内容
+ /// 事件内容 / Event Content
+ ///
+ /// 通讯事件的详细字节数据
+ /// Detailed byte data of communication events
+ ///
///
public byte[] Events { get; set; }
}
///
- /// 通讯事件获取方法
+ /// 通讯事件获取方法接口 / Comm Event Get Method Interface
+ ///
+ /// 实现 Modbus 功能码 12 (Get Comm Event Log)
+ /// Implements Modbus function code 12 (Get Comm Event Log)
+ ///
+ /// 使用场景 / Use Cases:
+ ///
+ /// - 查看通讯事件历史记录 / View communication event history
+ /// - 故障诊断 / Troubleshooting
+ /// - 仅用于串行通信 (RTU/ASCII) / Serial communication only (RTU/ASCII)
+ ///
+ ///
+ ///
///
public interface IUtilityMethodCommEventLog
{
///
- /// 获取通讯事件
+ /// 获取通讯事件 / Get Comm Event
+ ///
+ /// 读取从站设备的通讯事件日志
+ /// Read comm event log from slave device
+ ///
///
- ///
+ ///
+ /// 通讯事件数据 / Comm Event Data
+ ///
+ /// ReturnStruct<CommEventLogData> 包含事件日志
+ /// ReturnStruct<CommEventLogData> contains event log
+ ///
+ ///
Task> GetCommEventLogAsync();
}
+ #endregion
+
+ #region 从站 ID 获取 / Slave ID Get
+
///
- /// 获取从站号数据
+ /// 从站 ID 数据类 / Slave ID Data Class
+ ///
+ /// 存储 Modbus 从站 ID 获取功能的返回数据
+ /// Stores return data for Modbus slave ID get functionality
+ ///
///
public class SlaveIdData
{
///
- /// 从站号
+ /// 从站号 / Slave ID
+ ///
+ /// 从站设备的唯一标识
+ /// Unique identifier of slave device
+ ///
///
public byte SlaveId { get; set; }
///
- /// 指示状态
+ /// 指示状态 / Indicator Status
+ ///
+ /// 从站设备的运行状态指示
+ /// Run status indicator of slave device
+ ///
///
public byte IndicatorStatus { get; set; }
///
- /// 附加信息
+ /// 附加信息 / Additional Data
+ ///
+ /// 从站设备的附加信息字节
+ /// Additional data bytes from slave device
+ ///
///
public byte[] AdditionalData { get; set; }
}
///
- /// 获取从站号方法
+ /// 从站 ID 获取方法接口 / Slave ID Get Method Interface
+ ///
+ /// 实现 Modbus 功能码 17 (Report Slave ID)
+ /// Implements Modbus function code 17 (Report Slave ID)
+ ///
+ /// 使用场景 / Use Cases:
+ ///
+ /// - 识别从站设备 / Identify slave device
+ /// - 获取设备信息 / Get device information
+ /// - 仅用于串行通信 (RTU/ASCII) / Serial communication only (RTU/ASCII)
+ ///
+ ///
+ ///
///
public interface IUtilityMethodSlaveId
{
///
- /// 获取从站号
+ /// 获取从站号 / Get Slave ID
+ ///
+ /// 读取从站设备的 ID 和附加信息
+ /// Read slave ID and additional information
+ ///
///
- ///
+ ///
+ /// 从站 ID 数据 / Slave ID Data
+ ///
+ /// ReturnStruct<SlaveIdData> 包含从站信息
+ /// ReturnStruct<SlaveIdData> contains slave information
+ ///
+ ///
Task> GetSlaveIdAsync();
}
+ #endregion
+
+ #region 文件记录读写 / File Record Read/Write
+
///
- /// 文件记录读写方法
+ /// 文件记录读写方法接口 / File Record Read/Write Method Interface
+ ///
+ /// 实现 Modbus 功能码 20/21 (Read/Write File Record)
+ /// Implements Modbus function code 20/21 (Read/Write File Record)
+ ///
+ /// 使用场景 / Use Cases:
+ ///
+ /// - 读取从站文件记录 / Read slave file records
+ /// - 写入从站文件记录 / Write slave file records
+ /// - 参数备份和恢复 / Parameter backup and restore
+ ///
+ ///
+ ///
///
public interface IUtilityMethodFileRecord
{
///
- /// 读文件记录
+ /// 读文件记录 / Read File Record
+ ///
+ /// 从从站设备读取文件记录
+ /// Read file records from slave device
+ ///
///
- /// 读文件记录定义
- ///
+ ///
+ /// 读文件记录定义 / Read File Record Definitions
+ ///
+ /// 指定要读取的文件记录
+ /// Specifies file records to read
+ ///
+ ///
+ ///
+ /// 文件记录输出定义数组 / File Record Output Definition Array
+ ///
Task> GetFileRecordAsync(ReadFileRecordInputDef[] recordDefs);
+
///
- /// 写文件记录
+ /// 写文件记录 / Write File Record
+ ///
+ /// 向从站设备写入文件记录
+ /// Write file records to slave device
+ ///
///
- /// 写文件记录定义
- ///
+ ///
+ /// 写文件记录定义 / Write File Record Definitions
+ ///
+ /// 指定要写入的文件记录
+ /// Specifies file records to write
+ ///
+ ///
+ ///
+ /// 文件记录输出定义数组 / File Record Output Definition Array
+ ///
Task> SetFileRecordAsync(WriteFileRecordInputDef[] recordDefs);
}
+ #endregion
+
+ #region 掩码写寄存器 / Mask Write Register
+
///
- /// 掩码写入数据
+ /// 掩码写寄存器数据类 / Mask Write Register Data Class
+ ///
+ /// 存储 Modbus 掩码写寄存器功能的数据
+ /// Stores data for Modbus mask write register functionality
+ ///
///
public class MaskRegisterData
{
///
- /// 地址索引
+ /// 地址索引 / Reference Address
+ ///
+ /// 要写入的寄存器地址
+ /// Register address to write
+ ///
///
public ushort ReferenceAddress { get; set; }
///
- /// 与掩码
+ /// 与掩码 / AND Mask
+ ///
+ /// 与操作掩码
+ /// AND operation mask
+ ///
///
public ushort AndMask { get; set; }
///
- /// 或掩码
+ /// 或掩码 / OR Mask
+ ///
+ /// 或操作掩码
+ /// OR operation mask
+ ///
///
public ushort OrMask { get; set; }
}
///
- /// 掩码写入方法
+ /// 掩码写寄存器方法接口 / Mask Write Register Method Interface
+ ///
+ /// 实现 Modbus 功能码 22 (Mask Write Register)
+ /// Implements Modbus function code 22 (Mask Write Register)
+ ///
+ /// 操作公式 / Operation Formula:
+ /// New Value = (Current Value AND AndMask) OR (OrMask AND (NOT AndMask))
+ ///
+ ///
+ /// 使用场景 / Use Cases:
+ ///
+ /// - 修改寄存器的特定位 / Modify specific bits of register
+ /// - 无需读取 - 修改 - 写入操作 / No need for read-modify-write operation
+ ///
+ ///
+ ///
///
public interface IUtilityMethodMaskRegister
{
///
- /// 写入掩码
+ /// 写入掩码 / Write Mask
+ ///
+ /// 使用掩码写入寄存器
+ /// Write to register using mask
+ ///
///
- /// 地址索引
- /// 与掩码
- /// 或掩码
- ///
+ ///
+ /// 地址索引 / Reference Address
+ ///
+ /// 要写入的寄存器地址
+ /// Register address to write
+ ///
+ ///
+ ///
+ /// 与掩码 / AND Mask
+ ///
+ /// 与操作掩码
+ /// AND operation mask
+ ///
+ ///
+ ///
+ /// 或掩码 / OR Mask
+ ///
+ /// 或操作掩码
+ /// OR operation mask
+ ///
+ ///
+ ///
+ /// 掩码寄存器数据 / Mask Register Data
+ ///
+ /// ReturnStruct<MaskRegisterData> 包含写入结果
+ /// ReturnStruct<MaskRegisterData> contains write result
+ ///
+ ///
Task> SetMaskRegister(ushort referenceAddress, ushort andMask, ushort orMask);
}
+ #endregion
+
+ #region 读写多寄存器 / Read/Write Multiple Registers
+
///
- /// 寄存器读写方法
+ /// 寄存器读写方法接口 / Register Read/Write Method Interface
+ ///
+ /// 实现 Modbus 功能码 23 (Read/Write Multiple Registers)
+ /// Implements Modbus function code 23 (Read/Write Multiple Registers)
+ ///
+ /// 使用场景 / Use Cases:
+ ///
+ /// - 原子操作:先读后写 / Atomic operation: read then write
+ /// - 减少通讯次数 / Reduce communication times
+ /// - 确保数据一致性 / Ensure data consistency
+ ///
+ ///
+ ///
///
public interface IUtilityMethodMultipleRegister
{
///
- /// 读写多寄存器
+ /// 读写多寄存器 / Read/Write Multiple Registers
+ ///
+ /// 同时执行读取和写入操作
+ /// Execute read and write operations simultaneously
+ ///
///
- /// 读起始地址
- /// 读数量
- /// 写寄存器地址
- /// 写数据
- ///
+ ///
+ /// 读起始地址 / Read Starting Address
+ ///
+ /// 开始读取的寄存器地址
+ /// Starting register address for read
+ ///
+ ///
+ ///
+ /// 读数量 / Quantity to Read
+ ///
+ /// 要读取的寄存器数量
+ /// Number of registers to read
+ ///
+ ///
+ ///
+ /// 写起始地址 / Write Starting Address
+ ///
+ /// 开始写入的寄存器地址
+ /// Starting register address for write
+ ///
+ ///
+ ///
+ /// 写值数组 / Write Values Array
+ ///
+ /// 要写入的寄存器值数组
+ /// Array of register values to write
+ ///
+ ///
+ ///
+ /// 读取的寄存器值数组 / Read Register Values Array
+ ///
+ /// ReturnStruct<ushort[]> 包含读取结果
+ /// ReturnStruct<ushort[]> contains read result
+ ///
+ ///
Task> GetMultipleRegister(ushort readStartingAddress, ushort quantityToRead, ushort writeStartingAddress, ushort[] writeValues);
}
+ #endregion
+
+ #region FIFO 队列读 / FIFO Queue Read
+
///
- /// FIFO队列读取方法
+ /// FIFO 队列读方法接口 / FIFO Queue Read Method Interface
+ ///
+ /// 实现 Modbus 功能码 24 (Read FIFO Queue)
+ /// Implements Modbus function code 24 (Read FIFO Queue)
+ ///
+ /// 使用场景 / Use Cases:
+ ///
+ /// - 读取 FIFO 队列数据 / Read FIFO queue data
+ /// - 事件缓冲区读取 / Event buffer read
+ ///
+ ///
+ ///
///
public interface IUtilityMethodFIFOQueue
{
///
- /// 读FIFO队列
+ /// 读 FIFO 队列 / Read FIFO Queue
+ ///
+ /// 从从站设备读取 FIFO 队列数据
+ /// Read FIFO queue data from slave device
+ ///
///
- /// FIFO队列地址
- ///
+ ///
+ /// FIFO 指针地址 / FIFO Pointer Address
+ ///
+ /// FIFO 队列的指针地址
+ /// Pointer address of FIFO queue
+ ///
+ ///
+ ///
+ /// FIFO 值寄存器数组 / FIFO Value Register Array
+ ///
+ /// ReturnStruct<ushort[]> 包含 FIFO 数据
+ /// ReturnStruct<ushort[]> contains FIFO data
+ ///
+ ///
Task> GetFIFOQueue(ushort fifoPointerAddress);
}
+
+ #endregion
}
diff --git a/Modbus.Net/Modbus.Net.Modbus/ModbusAsciiInTcpProtocol.cs b/Modbus.Net/Modbus.Net.Modbus/ModbusAsciiInTcpProtocol.cs
index 98c70c2..d53990f 100644
--- a/Modbus.Net/Modbus.Net.Modbus/ModbusAsciiInTcpProtocol.cs
+++ b/Modbus.Net/Modbus.Net.Modbus/ModbusAsciiInTcpProtocol.cs
@@ -1,26 +1,91 @@
-namespace Modbus.Net.Modbus
+namespace Modbus.Net.Modbus
{
///
- /// Modbus/Ascii码协议Tcp透传
+ /// Modbus ASCII over TCP 协议类 / Modbus ASCII over TCP Protocol Class
+ ///
+ /// 实现 Modbus ASCII 协议通过 TCP 透传的功能,用于串口服务器场景
+ /// Implements Modbus ASCII protocol over TCP tunneling, used for serial device server scenarios
+ ///
+ /// 使用场景 / Use Cases:
+ ///
+ /// - 串口服务器 / Serial device server
+ /// - 通过 TCP 网络传输 ASCII 帧 / Transmit ASCII frames over TCP network
+ /// - 远程串口访问 / Remote serial access
+ /// - 多个 ASCII 设备共享一个 TCP 连接 / Multiple ASCII devices sharing one TCP connection
+ ///
+ ///
+ ///
+ /// 与 Modbus ASCII 的区别 / Difference from Modbus ASCII:
+ ///
+ /// - ASCII over TCP - ASCII 帧原样传输,通过 TCP / ASCII frames as-is, over TCP
+ /// - Modbus ASCII - 直接串口传输 ASCII 帧 / Direct serial transmission of ASCII frames
+ ///
+ ///
+ ///
+ /// 帧格式 / Frame Format:
+ ///
+ /// : [从站地址][功能码][数据][LRC][CR][LF]
+ /// │ │ │ │ │ │
+ /// │ │ │ │ │ └─ 换行符 (0x0A)
+ /// │ │ │ │ └─ 回车符 (0x0D)
+ /// │ │ │ └─ LRC 校验 (2 字符十六进制)
+ /// │ │ └─ 数据 (十六进制 ASCII 字符)
+ /// │ └─ 功能码 (2 字符十六进制)
+ /// └─ 起始符 (冒号 0x3A)
+ /// (通过 TCP 原样传输)
+ /// (Transmitted as-is over TCP)
+ ///
+ ///
+ ///
+ /// 使用示例 / Usage Example:
+ ///
+ /// // 通过串口服务器连接 ASCII 设备 / Connect ASCII device via serial server
+ /// var protocol = new ModbusAsciiInTcpProtocol(
+ /// "192.168.1.200", // 串口服务器 IP / Serial server IP
+ /// 8899, // 串口服务器端口 / Serial server port
+ /// slaveAddress: 1,
+ /// masterAddress: 0
+ /// );
+ ///
+ /// // 连接设备 / Connect to device
+ /// await protocol.ConnectAsync();
+ ///
+ /// // 读取数据 (ASCII 帧通过 TCP 透传) / Read data (ASCII frames tunneled over TCP)
+ /// var inputStruct = new ReadDataModbusInputStruct(1, "4X 1", 10, addressTranslator, 0);
+ /// var outputStruct = await protocol.SendReceiveAsync<ReadDataModbusOutputStruct>(
+ /// protocol[typeof(ReadDataModbusProtocol)],
+ /// inputStruct
+ /// );
+ ///
+ ///
+ ///
///
public class ModbusAsciiInTcpProtocol : ModbusProtocol
{
///
- /// 构造函数
+ /// 构造函数 (从配置读取 IP) / Constructor (Read IP from Configuration)
+ ///
+ /// 从配置文件读取 IP 地址创建 Modbus ASCII over TCP 协议实例
+ /// Create Modbus ASCII over TCP protocol instance with IP address read from configuration file
+ ///
///
- /// 从站号
- /// 主站号
+ /// 从站号 / Slave Address
+ /// 主站号 / Master Address
public ModbusAsciiInTcpProtocol(byte slaveAddress, byte masterAddress)
: this(ConfigurationReader.GetValueDirect("TCP:Modbus", "IP"), slaveAddress, masterAddress)
{
}
///
- /// 构造函数
+ /// 构造函数 (指定 IP) / Constructor (Specify IP)
+ ///
+ /// 使用指定的 IP 地址创建 Modbus ASCII over TCP 协议实例
+ /// Create Modbus ASCII over TCP protocol instance with specified IP address
+ ///
///
- /// ip地址
- /// 从站号
- /// 主站号
+ /// IP 地址 / IP Address (串口服务器地址 / Serial server address)
+ /// 从站号 / Slave Address
+ /// 主站号 / Master Address
public ModbusAsciiInTcpProtocol(string ip, byte slaveAddress, byte masterAddress)
: base(slaveAddress, masterAddress)
{
@@ -28,16 +93,20 @@
}
///
- /// 构造函数
+ /// 构造函数 (指定 IP 和端口) / Constructor (Specify IP and Port)
+ ///
+ /// 使用指定的 IP 地址和端口创建 Modbus ASCII over TCP 协议实例
+ /// Create Modbus ASCII over TCP protocol instance with specified IP address and port
+ ///
///
- /// ip地址
- /// 端口
- /// 从站号
- /// 主站号
+ /// IP 地址 / IP Address
+ /// 端口号 / Port Number (串口服务器端口 / Serial server port)
+ /// 从站号 / Slave Address
+ /// 主站号 / Master Address
public ModbusAsciiInTcpProtocol(string ip, int port, byte slaveAddress, byte masterAddress)
: base(slaveAddress, masterAddress)
{
ProtocolLinker = new ModbusAsciiInTcpProtocolLinker(ip, port);
}
}
-}
\ No newline at end of file
+}
diff --git a/Modbus.Net/Modbus.Net.Modbus/ModbusAsciiInTcpProtocolLinker.cs b/Modbus.Net/Modbus.Net.Modbus/ModbusAsciiInTcpProtocolLinker.cs
index 2e6cb9f..873981c 100644
--- a/Modbus.Net/Modbus.Net.Modbus/ModbusAsciiInTcpProtocolLinker.cs
+++ b/Modbus.Net/Modbus.Net.Modbus/ModbusAsciiInTcpProtocolLinker.cs
@@ -1,44 +1,163 @@
-using System.Text;
+using System.Text;
namespace Modbus.Net.Modbus
{
///
- /// Modbus/Ascii码协议连接器Tcp透传
+ /// Modbus ASCII over TCP 协议连接器 / Modbus ASCII over TCP Protocol Linker
+ ///
+ /// 实现 Modbus ASCII 协议通过 TCP 透传的连接器,继承自 TcpProtocolLinker
+ /// Implements Modbus ASCII protocol over TCP tunneling linker, inherits from TcpProtocolLinker
+ ///
+ /// 主要功能 / Main Functions:
+ ///
+ /// - TCP 连接管理 / TCP connection management
+ /// - ASCII 帧原样传输 / ASCII frame transparent transmission
+ /// - 无 MBAP 头 / No MBAP header
+ /// - 保留 LRC 校验 / LRC checksum preserved
+ /// - 响应校验 / Response validation
+ /// - ASCII 编码转换 / ASCII encoding conversion
+ ///
+ ///
+ ///
+ /// 使用场景 / Use Cases:
+ ///
+ /// - 串口服务器 / Serial device server
+ /// - 通过 TCP 网络传输 ASCII 帧 / Transmit ASCII frames over TCP network
+ /// - 远程串口访问 / Remote serial access
+ ///
+ ///
+ ///
+ /// 帧格式 / Frame Format:
+ ///
+ /// : [从站地址][功能码][数据][LRC][CR][LF]
+ /// │ │ │ │ │ │
+ /// │ │ │ │ │ └─ 换行符 (0x0A)
+ /// │ │ │ │ └─ 回车符 (0x0D)
+ /// │ │ │ └─ LRC 校验 (2 字符十六进制)
+ /// │ │ └─ 数据 (十六进制 ASCII 字符)
+ /// │ └─ 功能码 (2 字符十六进制)
+ /// └─ 起始符 (冒号 0x3A)
+ /// (通过 TCP 原样传输)
+ /// (Transmitted as-is over TCP)
+ ///
+ ///
+ ///
///
public class ModbusAsciiInTcpProtocolLinker : TcpProtocolLinker
{
///
- /// 构造函数
+ /// 构造函数 (从配置读取端口) / Constructor (Read Port from Configuration)
+ ///
+ /// 从配置文件读取端口创建 Modbus ASCII over TCP 连接器
+ /// Create Modbus ASCII over TCP linker with port read from configuration file
+ ///
+ /// 配置项 / Configuration Items:
+ ///
+ /// - TCP:{IP}:ModbusPort - 指定 IP 的端口 / Port for specified IP
+ /// - TCP:Modbus:ModbusPort - 默认 Modbus 端口 / Default Modbus port
+ /// - 默认端口:502 / Default port: 502
+ ///
+ ///
+ ///
///
- /// IP地址
+ ///
+ /// IP 地址 / IP Address
+ ///
+ /// 串口服务器的 IP 地址
+ /// Serial device server IP address
+ ///
+ ///
public ModbusAsciiInTcpProtocolLinker(string ip)
: base(ip, int.Parse(ConfigurationReader.GetValueDirect("TCP:" + ip, "ModbusPort") ?? ConfigurationReader.GetValueDirect("TCP:Modbus", "ModbusPort")))
{
}
///
- /// 构造函数
+ /// 构造函数 (指定 IP 和端口) / Constructor (Specify IP and Port)
+ ///
+ /// 使用指定的 IP 地址和端口创建 Modbus ASCII over TCP 连接器
+ /// Create Modbus ASCII over TCP linker with specified IP address and port
+ ///
///
- /// ip地址
- /// 端口号
+ ///
+ /// IP 地址 / IP Address
+ /// 串口服务器的 IP 地址 / Serial device server IP address
+ ///
+ ///
+ /// 端口号 / Port Number
+ ///
+ /// 串口服务器端口,常用 8899, 502 等
+ /// Serial server port, commonly 8899, 502, etc.
+ ///
+ ///
public ModbusAsciiInTcpProtocolLinker(string ip, int port)
: base(ip, port)
{
}
///
- /// 校验返回数据是否正确
+ /// 校验返回数据是否正确 / Validate Return Data
+ ///
+ /// 校验从设备返回的 Modbus ASCII over TCP 响应数据
+ /// Validate Modbus ASCII over TCP response data returned from device
+ ///
+ /// 校验流程 / Validation Flow:
+ ///
+ /// - 调用基类校验 (TCP 连接状态) / Call base validation (TCP connection status)
+ /// - 转换为 ASCII 字符串 / Convert to ASCII string
+ /// - 检查功能码 (第 4-5 字符) / Check function code (characters 4-5)
+ /// - 如果功能码>127,表示错误响应 / If function code>127, indicates error response
+ /// - 抛出 ModbusProtocolErrorException / Throw ModbusProtocolErrorException
+ ///
+ ///
+ ///
+ /// ASCII 格式说明 / ASCII Format Description:
+ ///
+ /// - 字符 0: 冒号 ':' / Character 0: Colon ':'
+ /// - 字符 1-2: 从站地址 / Characters 1-2: Slave address
+ /// - 字符 3-4: 功能码 / Characters 3-4: Function code
+ /// - 字符 5+: 数据 / Characters 5+: Data
+ ///
+ ///
+ ///
///
- /// 返回的数据
- /// 校验是否正确
+ ///
+ /// 返回的数据 / Returned Data
+ ///
+ /// ASCII 编码的 Modbus ASCII 响应
+ /// ASCII-encoded Modbus ASCII response
+ ///
+ ///
+ ///
+ /// 校验是否正确 / Whether Validation is Correct
+ ///
+ ///
+ /// - true: 数据正确 / Data correct
+ /// - false: 数据错误 / Data error
+ /// - null: 无法判断 / Cannot determine
+ ///
+ ///
+ ///
+ ///
+ /// 当功能码>127 时抛出 Modbus 协议错误
+ /// Throw Modbus protocol error when function code>127
+ ///
public override bool? CheckRight(byte[] content)
{
+ // 基类校验 (TCP 连接状态) / Base validation (TCP connection status)
if (base.CheckRight(content) != true) return base.CheckRight(content);
- //Modbus协议错误
+
+ // 转换为 ASCII 字符串 / Convert to ASCII string
var contentString = Encoding.ASCII.GetString(content);
+
+ // Modbus 协议错误检测 / Modbus protocol error detection
+ // 功能码在第 4-5 字符 (从 0 开始计数是 3-4)
+ // Function code at characters 4-5 (0-based index 3-4)
if (byte.Parse(contentString.Substring(3, 2)) > 127)
+ // 功能码>127 表示异常响应 / Function code>127 indicates exception response
throw new ModbusProtocolErrorException(byte.Parse(contentString.Substring(5, 2)));
+
return true;
}
}
-}
\ No newline at end of file
+}
diff --git a/Modbus.Net/Modbus.Net.Modbus/ModbusAsciiInUdpProtocol.cs b/Modbus.Net/Modbus.Net.Modbus/ModbusAsciiInUdpProtocol.cs
index d6bc2d2..ff61def 100644
--- a/Modbus.Net/Modbus.Net.Modbus/ModbusAsciiInUdpProtocol.cs
+++ b/Modbus.Net/Modbus.Net.Modbus/ModbusAsciiInUdpProtocol.cs
@@ -1,26 +1,96 @@
-namespace Modbus.Net.Modbus
+namespace Modbus.Net.Modbus
{
///
- /// Modbus/Ascii码协议Udp透传
+ /// Modbus ASCII over UDP 协议类 / Modbus ASCII over UDP Protocol Class
+ ///
+ /// 实现 Modbus ASCII 协议通过 UDP 透传的功能,用于无连接的网络通信
+ /// Implements Modbus ASCII protocol over UDP tunneling, used for connectionless network communication
+ ///
+ /// 使用场景 / Use Cases:
+ ///
+ /// - UDP 广播查询 / UDP broadcast query
+ /// - 通过 UDP 网络传输 ASCII 帧 / Transmit ASCII frames over UDP network
+ /// - 多个 ASCII 设备共享一个 UDP 端口 / Multiple ASCII devices sharing one UDP port
+ /// - 不要求可靠性的场景 / Scenarios not requiring reliability
+ ///
+ ///
+ ///
+ /// 与 Modbus ASCII over TCP 的区别 / Difference from Modbus ASCII over TCP:
+ ///
+ /// - ASCII over UDP - 无连接,不保证送达,支持广播 / Connectionless, no delivery guarantee, supports broadcast
+ /// - ASCII over TCP - 面向连接,保证送达,不支持广播 / Connection-oriented, delivery guaranteed, no broadcast
+ ///
+ ///
+ ///
+ /// 帧格式 / Frame Format:
+ ///
+ /// : [从站地址][功能码][数据][LRC][CR][LF]
+ /// │ │ │ │ │ │
+ /// │ │ │ │ │ └─ 换行符 (0x0A)
+ /// │ │ │ │ └─ 回车符 (0x0D)
+ /// │ │ │ └─ LRC 校验 (2 字符十六进制)
+ /// │ │ └─ 数据 (十六进制 ASCII 字符)
+ /// │ └─ 功能码 (2 字符十六进制)
+ /// └─ 起始符 (冒号 0x3A)
+ /// (通过 UDP 原样传输)
+ /// (Transmitted as-is over UDP)
+ ///
+ ///
+ ///
+ /// 使用示例 / Usage Example:
+ ///
+ /// // 通过 UDP 连接 ASCII 设备 / Connect ASCII device via UDP
+ /// var protocol = new ModbusAsciiInUdpProtocol(
+ /// "192.168.1.200", // 设备 IP / Device IP
+ /// 502, // UDP 端口 / UDP port
+ /// slaveAddress: 1,
+ /// masterAddress: 0
+ /// );
+ ///
+ /// // 连接设备 (UDP 无需真正连接) / Connect to device (UDP doesn't need real connection)
+ /// await protocol.ConnectAsync();
+ ///
+ /// // 读取数据 (ASCII 帧通过 UDP 透传) / Read data (ASCII frames tunneled over UDP)
+ /// var inputStruct = new ReadDataModbusInputStruct(1, "4X 1", 10, addressTranslator, 0);
+ /// var outputStruct = await protocol.SendReceiveAsync<ReadDataModbusOutputStruct>(
+ /// protocol[typeof(ReadDataModbusProtocol)],
+ /// inputStruct
+ /// );
+ ///
+ /// // UDP 广播查询示例 / UDP broadcast query example
+ /// var broadcastProtocol = new ModbusAsciiInUdpProtocol("255.255.255.255", 502, slaveAddress: 0, masterAddress: 0);
+ /// // 注意:广播地址为 0,所有从站都会响应
+ /// // Note: Broadcast address is 0, all slaves will respond
+ ///
+ ///
+ ///
///
public class ModbusAsciiInUdpProtocol : ModbusProtocol
{
///
- /// 构造函数
+ /// 构造函数 (从配置读取 IP) / Constructor (Read IP from Configuration)
+ ///
+ /// 从配置文件读取 IP 地址创建 Modbus ASCII over UDP 协议实例
+ /// Create Modbus ASCII over UDP protocol instance with IP address read from configuration file
+ ///
///
- /// 从站号
- /// 主站号
+ /// 从站号 / Slave Address
+ /// 主站号 / Master Address
public ModbusAsciiInUdpProtocol(byte slaveAddress, byte masterAddress)
: this(ConfigurationReader.GetValueDirect("UDP:Modbus", "IP"), slaveAddress, masterAddress)
{
}
///
- /// 构造函数
+ /// 构造函数 (指定 IP) / Constructor (Specify IP)
+ ///
+ /// 使用指定的 IP 地址创建 Modbus ASCII over UDP 协议实例
+ /// Create Modbus ASCII over UDP protocol instance with specified IP address
+ ///
///
- /// ip地址
- /// 从站号
- /// 主站号
+ /// IP 地址 / IP Address (可使用广播地址 255.255.255.255)
+ /// 从站号 / Slave Address
+ /// 主站号 / Master Address
public ModbusAsciiInUdpProtocol(string ip, byte slaveAddress, byte masterAddress)
: base(slaveAddress, masterAddress)
{
@@ -28,12 +98,16 @@
}
///
- /// 构造函数
+ /// 构造函数 (指定 IP 和端口) / Constructor (Specify IP and Port)
+ ///
+ /// 使用指定的 IP 地址和端口创建 Modbus ASCII over UDP 协议实例
+ /// Create Modbus ASCII over UDP protocol instance with specified IP address and port
+ ///
///
- /// ip地址
- /// 端口
- /// 从站号
- /// 主站号
+ /// IP 地址 / IP Address
+ /// 端口号 / Port Number
+ /// 从站号 / Slave Address
+ /// 主站号 / Master Address
public ModbusAsciiInUdpProtocol(string ip, int port, byte slaveAddress, byte masterAddress)
: base(slaveAddress, masterAddress)
{
diff --git a/Modbus.Net/Modbus.Net.Modbus/ModbusAsciiInUdpProtocolLinker.cs b/Modbus.Net/Modbus.Net.Modbus/ModbusAsciiInUdpProtocolLinker.cs
index dc3efaf..16a2fe6 100644
--- a/Modbus.Net/Modbus.Net.Modbus/ModbusAsciiInUdpProtocolLinker.cs
+++ b/Modbus.Net/Modbus.Net.Modbus/ModbusAsciiInUdpProtocolLinker.cs
@@ -1,43 +1,171 @@
-using System.Text;
+using System.Text;
namespace Modbus.Net.Modbus
{
///
- /// Modbus/Ascii码协议连接器Udp透传
+ /// Modbus ASCII over UDP 协议连接器 / Modbus ASCII over UDP Protocol Linker
+ ///
+ /// 实现 Modbus ASCII 协议通过 UDP 透传的连接器,继承自 UdpProtocolLinker
+ /// Implements Modbus ASCII protocol over UDP tunneling linker, inherits from UdpProtocolLinker
+ ///
+ /// 主要功能 / Main Functions:
+ ///
+ /// - UDP 连接管理 / UDP connection management
+ /// - ASCII 帧原样传输 / ASCII frame transparent transmission
+ /// - 无 MBAP 头 / No MBAP header
+ /// - 保留 LRC 校验 / LRC checksum preserved
+ /// - 支持广播查询 / Supports broadcast query
+ /// - 响应校验 / Response validation
+ /// - ASCII 编码转换 / ASCII encoding conversion
+ ///
+ ///
+ ///
+ /// 使用场景 / Use Cases:
+ ///
+ /// - UDP 广播查询 / UDP broadcast query
+ /// - 通过 UDP 网络传输 ASCII 帧 / Transmit ASCII frames over UDP network
+ /// - 多个 ASCII 设备共享一个 UDP 端口 / Multiple ASCII devices sharing one UDP port
+ /// - 不要求可靠性的场景 / Scenarios not requiring reliability
+ ///
+ ///
+ ///
+ /// 与 ASCII over TCP 的区别 / Difference from ASCII over TCP:
+ ///
+ /// - ASCII over UDP - 无连接,不保证送达,支持广播 / Connectionless, no delivery guarantee, supports broadcast
+ /// - ASCII over TCP - 面向连接,保证送达,不支持广播 / Connection-oriented, delivery guaranteed, no broadcast
+ ///
+ ///
+ ///
+ /// 帧格式 / Frame Format:
+ ///
+ /// : [从站地址][功能码][数据][LRC][CR][LF]
+ /// │ │ │ │ │ │
+ /// │ │ │ │ │ └─ 换行符 (0x0A)
+ /// │ │ │ │ └─ 回车符 (0x0D)
+ /// │ │ │ └─ LRC 校验 (2 字符十六进制)
+ /// │ │ └─ 数据 (十六进制 ASCII 字符)
+ /// │ └─ 功能码 (2 字符十六进制)
+ /// └─ 起始符 (冒号 0x3A)
+ /// (通过 UDP 原样传输)
+ /// (Transmitted as-is over UDP)
+ ///
+ ///
+ ///
///
public class ModbusAsciiInUdpProtocolLinker : UdpProtocolLinker
{
///
- /// 构造函数
+ /// 构造函数 (从配置读取端口) / Constructor (Read Port from Configuration)
+ ///
+ /// 从配置文件读取端口创建 Modbus ASCII over UDP 连接器
+ /// Create Modbus ASCII over UDP linker with port read from configuration file
+ ///
+ /// 配置项 / Configuration Items:
+ ///
+ /// - UDP:{IP}:ModbusPort - 指定 IP 的端口 / Port for specified IP
+ /// - UDP:Modbus:ModbusPort - 默认 Modbus 端口 / Default Modbus port
+ /// - 默认端口:502 / Default port: 502
+ ///
+ ///
+ ///
///
- /// IP地址
+ ///
+ /// IP 地址 / IP Address
+ ///
+ /// 目标设备的 IP 地址 (可使用广播地址 255.255.255.255)
+ /// Target device IP address (can use broadcast address 255.255.255.255)
+ ///
+ ///
public ModbusAsciiInUdpProtocolLinker(string ip)
: base(ip, int.Parse(ConfigurationReader.GetValueDirect("UDP:" + ip, "ModbusPort") ?? ConfigurationReader.GetValueDirect("UDP:Modbus", "ModbusPort")))
{
}
///
- /// 构造函数
+ /// 构造函数 (指定 IP 和端口) / Constructor (Specify IP and Port)
+ ///
+ /// 使用指定的 IP 地址和端口创建 Modbus ASCII over UDP 连接器
+ /// Create Modbus ASCII over UDP linker with specified IP address and port
+ ///
///
- /// ip地址
- /// 端口号
+ ///
+ /// IP 地址 / IP Address
+ /// 目标设备的 IP 地址 / Target device IP address
+ ///
+ ///
+ /// 端口号 / Port Number
+ ///
+ /// UDP 端口,默认 502
+ /// UDP port, default 502
+ ///
+ ///
public ModbusAsciiInUdpProtocolLinker(string ip, int port)
: base(ip, port)
{
}
///
- /// 校验返回数据是否正确
+ /// 校验返回数据是否正确 / Validate Return Data
+ ///
+ /// 校验从设备返回的 Modbus ASCII over UDP 响应数据
+ /// Validate Modbus ASCII over UDP response data returned from device
+ ///
+ /// 校验流程 / Validation Flow:
+ ///
+ /// - 调用基类校验 (UDP 连接状态) / Call base validation (UDP connection status)
+ /// - 转换为 ASCII 字符串 / Convert to ASCII string
+ /// - 检查功能码 (第 4-5 字符) / Check function code (characters 4-5)
+ /// - 如果功能码>127,表示错误响应 / If function code>127, indicates error response
+ /// - 抛出 ModbusProtocolErrorException / Throw ModbusProtocolErrorException
+ ///
+ ///
+ ///
+ /// ASCII 格式说明 / ASCII Format Description:
+ ///
+ /// - 字符 0: 冒号 ':' / Character 0: Colon ':'
+ /// - 字符 1-2: 从站地址 / Characters 1-2: Slave address
+ /// - 字符 3-4: 功能码 / Characters 3-4: Function code
+ /// - 字符 5+: 数据 / Characters 5+: Data
+ ///
+ ///
+ ///
///
- /// 返回的数据
- /// 校验是否正确
+ ///
+ /// 返回的数据 / Returned Data
+ ///
+ /// ASCII 编码的 Modbus ASCII 响应
+ /// ASCII-encoded Modbus ASCII response
+ ///
+ ///
+ ///
+ /// 校验是否正确 / Whether Validation is Correct
+ ///
+ ///
+ /// - true: 数据正确 / Data correct
+ /// - false: 数据错误 / Data error
+ /// - null: 无法判断 / Cannot determine
+ ///
+ ///
+ ///
+ ///
+ /// 当功能码>127 时抛出 Modbus 协议错误
+ /// Throw Modbus protocol error when function code>127
+ ///
public override bool? CheckRight(byte[] content)
{
+ // 基类校验 (UDP 连接状态) / Base validation (UDP connection status)
if (base.CheckRight(content) != true) return base.CheckRight(content);
- //Modbus协议错误
+
+ // 转换为 ASCII 字符串 / Convert to ASCII string
var contentString = Encoding.ASCII.GetString(content);
+
+ // Modbus 协议错误检测 / Modbus protocol error detection
+ // 功能码在第 4-5 字符 (从 0 开始计数是 3-4)
+ // Function code at characters 4-5 (0-based index 3-4)
if (byte.Parse(contentString.Substring(3, 2)) > 127)
+ // 功能码>127 表示异常响应 / Function code>127 indicates exception response
throw new ModbusProtocolErrorException(byte.Parse(contentString.Substring(5, 2)));
+
return true;
}
}
diff --git a/Modbus.Net/Modbus.Net.Modbus/ModbusAsciiProtocol.cs b/Modbus.Net/Modbus.Net.Modbus/ModbusAsciiProtocol.cs
index f3d7df6..771fd2f 100644
--- a/Modbus.Net/Modbus.Net.Modbus/ModbusAsciiProtocol.cs
+++ b/Modbus.Net/Modbus.Net.Modbus/ModbusAsciiProtocol.cs
@@ -1,30 +1,115 @@
-namespace Modbus.Net.Modbus
+namespace Modbus.Net.Modbus
{
///
- /// Modbus/Ascii码协议
+ /// Modbus/ASCII 协议类 / Modbus/ASCII Protocol Class
+ ///
+ /// 实现 Modbus ASCII 协议,用于串行通信
+ /// Implements Modbus ASCII protocol for serial communication
+ ///
+ /// 协议特点 / Protocol Characteristics:
+ ///
+ /// - ASCII 字符编码 / ASCII character encoding
+ /// - LRC 校验 / LRC checksum
+ /// - 以冒号 (:) 开始,CRLF 结束 / Starts with colon (:), ends with CRLF
+ /// - 效率较低,但易于调试 / Lower efficiency, but easy to debug
+ /// - 适用于低速串口通信 / Suitable for low-speed serial communication
+ ///
+ ///
+ ///
+ /// 帧格式 / Frame Format:
+ ///
+ /// : [从站地址][功能码][数据][LRC][CR][LF]
+ /// │ │ │ │ │ │
+ /// │ │ │ │ │ └─ 换行符 (0x0A)
+ /// │ │ │ │ └─ 回车符 (0x0D)
+ /// │ │ │ └─ LRC 校验 (2 字符十六进制)
+ /// │ │ └─ 数据 (十六进制 ASCII 字符)
+ /// │ └─ 功能码 (2 字符十六进制)
+ /// └─ 起始符 (冒号 0x3A)
+ ///
+ ///
+ ///
+ /// 与 RTU 的区别 / Difference from RTU:
+ ///
+ /// - ASCII: 人类可读,效率低 / ASCII: Human readable, low efficiency
+ /// - RTU: 二进制编码,效率高 / RTU: Binary encoding, high efficiency
+ /// - ASCII: LRC 校验 / ASCII: LRC checksum
+ /// - RTU: CRC16 校验 / RTU: CRC16 checksum
+ ///
+ ///
+ ///
+ /// 使用示例 / Usage Example:
+ ///
+ /// // 创建 Modbus ASCII 协议实例 / Create Modbus ASCII protocol instance
+ /// var protocol = new ModbusAsciiProtocol("COM1", slaveAddress: 1, masterAddress: 0);
+ ///
+ /// // 或者从配置读取 / Or read from configuration
+ /// var protocolFromConfig = new ModbusAsciiProtocol(slaveAddress: 1, masterAddress: 0);
+ /// // 配置项:COM:Modbus:COM = "COM1"
+ ///
+ /// // 连接设备 / Connect to device
+ /// await protocol.ConnectAsync();
+ ///
+ /// // 发送读取请求 / Send read request
+ /// var inputStruct = new ReadDataModbusInputStruct(1, "4X 1", 10, addressTranslator, 0);
+ /// var outputStruct = await protocol.SendReceiveAsync<ReadDataModbusOutputStruct>(
+ /// protocol[typeof(ReadDataModbusProtocol)],
+ /// inputStruct
+ /// );
+ ///
+ ///
+ ///
///
public class ModbusAsciiProtocol : ModbusProtocol
{
///
- /// 构造函数
+ /// 构造函数 (从配置读取串口) / Constructor (Read COM Port from Configuration)
+ ///
+ /// 从配置文件读取串口名称创建 Modbus ASCII 协议实例
+ /// Create Modbus ASCII protocol instance with COM port name read from configuration file
+ ///
+ /// 配置项 / Configuration Item:
+ /// COM:Modbus:COM = "COM1"
+ ///
+ ///
///
- /// 从站号
- /// 主站号
+ ///
+ /// 从站号 / Slave Address
+ /// Modbus 从站地址,范围 1-247 / Modbus slave address, range 1-247
+ ///
+ ///
+ /// 主站号 / Master Address
+ /// 通常为 0 或 1 / Usually 0 or 1
+ ///
public ModbusAsciiProtocol(byte slaveAddress, byte masterAddress)
: this(ConfigurationReader.GetValueDirect("COM:Modbus", "COM"), slaveAddress, masterAddress)
{
}
///
- /// 构造函数
+ /// 构造函数 (指定串口) / Constructor (Specify COM Port)
+ ///
+ /// 使用指定的串口名称创建 Modbus ASCII 协议实例
+ /// Create Modbus ASCII protocol instance with specified COM port name
+ ///
+ /// 串口配置从 appsettings.json 读取
+ /// Serial port configuration is read from appsettings.json
+ ///
+ ///
///
- /// 串口地址
- /// 从站号
- /// 主站号
+ ///
+ /// 串口地址 / COM Port Address
+ /// 如 "COM1", "COM2" 等 / e.g., "COM1", "COM2", etc.
+ ///
+ /// 从站号 / Slave Address
+ /// 主站号 / Master Address
public ModbusAsciiProtocol(string com, byte slaveAddress, byte masterAddress)
: base(slaveAddress, masterAddress)
{
+ // 创建 Modbus ASCII 协议链接器 / Create Modbus ASCII protocol linker
+ // 自动从配置读取串口参数 (波特率、校验位等)
+ // Automatically read serial port parameters from configuration (baud rate, parity, etc.)
ProtocolLinker = new ModbusAsciiProtocolLinker(com, slaveAddress);
}
}
-}
\ No newline at end of file
+}
diff --git a/Modbus.Net/Modbus.Net.Modbus/ModbusAsciiProtocolLinker.cs b/Modbus.Net/Modbus.Net.Modbus/ModbusAsciiProtocolLinker.cs
index f552984..44afc2a 100644
--- a/Modbus.Net/Modbus.Net.Modbus/ModbusAsciiProtocolLinker.cs
+++ b/Modbus.Net/Modbus.Net.Modbus/ModbusAsciiProtocolLinker.cs
@@ -1,35 +1,145 @@
-using System.Text;
+using System.Text;
namespace Modbus.Net.Modbus
{
///
- /// Modbus/Ascii码协议连接器
+ /// Modbus/ASCII 协议连接器 / Modbus/ASCII Protocol Linker
+ ///
+ /// 实现 Modbus ASCII 协议的连接器,继承自 ComProtocolLinker
+ /// Implements Modbus ASCII protocol linker, inherits from ComProtocolLinker
+ ///
+ /// 主要功能 / Main Functions:
+ ///
+ /// - 串口连接管理 / Serial connection management
+ /// - LRC 校验 / LRC checksum
+ /// - ASCII 编码转换 / ASCII encoding conversion
+ /// - 响应校验 / Response validation
+ /// - 错误检测 / Error detection
+ ///
+ ///
+ ///
+ /// ASCII 帧格式 / ASCII Frame Format:
+ ///
+ /// : [从站地址][功能码][数据][LRC][CR][LF]
+ /// │ │ │ │ │ │
+ /// │ │ │ │ │ └─ 换行符 (0x0A)
+ /// │ │ │ │ └─ 回车符 (0x0D)
+ /// │ │ │ └─ LRC 校验 (2 字符十六进制)
+ /// │ │ └─ 数据 (十六进制 ASCII 字符)
+ /// │ └─ 功能码 (2 字符十六进制)
+ /// └─ 起始符 (冒号 0x3A)
+ ///
+ ///
+ ///
+ /// 使用示例 / Usage Example:
+ ///
+ /// // 创建 Modbus ASCII 连接器 / Create Modbus ASCII linker
+ /// var linker = new ModbusAsciiProtocolLinker("COM1", slaveAddress: 1);
+ ///
+ /// // 连接设备 / Connect to device
+ /// await linker.ConnectAsync();
+ ///
+ /// // 发送数据 (ASCII 格式) / Send data (ASCII format)
+ /// string request = ":01030000000AF8[CR][LF]";
+ /// byte[] response = await linker.SendReceiveAsync(Encoding.ASCII.GetBytes(request));
+ ///
+ /// // CheckRight 会自动校验 LRC 和协议错误
+ /// // CheckRight will automatically validate LRC and protocol errors
+ ///
+ ///
+ ///
///
public class ModbusAsciiProtocolLinker : ComProtocolLinker
{
///
- /// 构造函数
+ /// 构造函数 / Constructor
+ ///
+ /// 初始化 Modbus ASCII 协议连接器
+ /// Initialize Modbus ASCII protocol linker
+ ///
///
- /// 串口地址
- /// 从站号
+ ///
+ /// 串口地址 / Serial Port Address
+ ///
+ /// 如 "COM1", "COM2" 等
+ /// e.g., "COM1", "COM2", etc.
+ ///
+ ///
+ ///
+ /// 从站号 / Slave Address
+ ///
+ /// Modbus 从站地址,范围 1-247
+ /// Modbus slave address, range 1-247
+ ///
+ ///
public ModbusAsciiProtocolLinker(string com, int slaveAddress)
: base(com, slaveAddress)
{
}
///
- /// 校验返回数据是否正确
+ /// 校验返回数据是否正确 / Validate Return Data
+ ///
+ /// 校验从设备返回的 Modbus ASCII 响应数据
+ /// Validate Modbus ASCII response data returned from device
+ ///
+ /// 校验流程 / Validation Flow:
+ ///
+ /// - 调用基类校验 (串口连接状态) / Call base validation (serial connection status)
+ /// - 转换为 ASCII 字符串 / Convert to ASCII string
+ /// - 检查功能码 (第 4-5 字符) / Check function code (characters 4-5)
+ /// - 如果功能码>127,表示错误响应 / If function code>127, indicates error response
+ /// - 抛出 ModbusProtocolErrorException / Throw ModbusProtocolErrorException
+ ///
+ ///
+ ///
+ /// ASCII 格式说明 / ASCII Format Description:
+ ///
+ /// - 字符 0: 冒号 ':' / Character 0: Colon ':'
+ /// - 字符 1-2: 从站地址 / Characters 1-2: Slave address
+ /// - 字符 3-4: 功能码 / Characters 3-4: Function code
+ /// - 字符 5+: 数据 / Characters 5+: Data
+ ///
+ ///
+ ///
///
- /// 返回的数据
- /// 校验是否正确
+ ///
+ /// 返回的数据 / Returned Data
+ ///
+ /// ASCII 编码的 Modbus ASCII 响应
+ /// ASCII-encoded Modbus ASCII response
+ ///
+ ///
+ ///
+ /// 校验是否正确 / Whether Validation is Correct
+ ///
+ ///
+ /// - true: 数据正确 / Data correct
+ /// - false: 数据错误 / Data error
+ /// - null: 无法判断 / Cannot determine
+ ///
+ ///
+ ///
+ ///
+ /// 当功能码>127 时抛出 Modbus 协议错误
+ /// Throw Modbus protocol error when function code>127
+ ///
public override bool? CheckRight(byte[] content)
{
+ // 基类校验 (串口连接状态) / Base validation (serial connection status)
if (base.CheckRight(content) != true) return base.CheckRight(content);
- //Modbus协议错误
+
+ // 转换为 ASCII 字符串 / Convert to ASCII string
var contentString = Encoding.ASCII.GetString(content);
+
+ // Modbus 协议错误检测 / Modbus protocol error detection
+ // 功能码在第 4-5 字符 (从 0 开始计数是 3-4)
+ // Function code at characters 4-5 (0-based index 3-4)
if (byte.Parse(contentString.Substring(3, 2)) > 127)
+ // 功能码>127 表示异常响应 / Function code>127 indicates exception response
throw new ModbusProtocolErrorException(byte.Parse(contentString.Substring(5, 2)));
+
return true;
}
}
-}
\ No newline at end of file
+}
diff --git a/Modbus.Net/Modbus.Net.Modbus/ModbusController.cs b/Modbus.Net/Modbus.Net.Modbus/ModbusController.cs
index 32677cd..d712732 100644
--- a/Modbus.Net/Modbus.Net.Modbus/ModbusController.cs
+++ b/Modbus.Net/Modbus.Net.Modbus/ModbusController.cs
@@ -1,68 +1,199 @@
-using System;
+using System;
using System.Collections.Generic;
using System.Linq;
namespace Modbus.Net.Modbus
{
///
- /// Modbus长度计算
+ /// Modbus 长度计算工具类 / Modbus Length Calculation Utility Class
+ ///
+ /// 提供 Modbus 协议各种模式的长度计算函数
+ /// Provides length calculation functions for various Modbus protocol modes
+ ///
+ /// 主要功能 / Main Functions:
+ ///
+ /// - ModbusAsciiLengthCalc - ASCII 协议长度计算 / ASCII protocol length calculation
+ /// - ModbusRtuResponseLengthCalc - RTU 响应长度计算 / RTU response length calculation
+ /// - ModbusRtuRequestLengthCalc - RTU 请求长度计算 / RTU request length calculation
+ ///
+ ///
+ ///
+ /// 使用场景 / Use Cases:
+ ///
+ /// - Controller 的 lengthCalc 参数 / Controller's lengthCalc parameter
+ /// - 数据帧切分 / Data frame splitting
+ /// - TCP 粘包处理 / TCP sticky packet handling
+ ///
+ ///
+ ///
///
public static class ModbusLengthCalc
{
///
- /// Modbus Ascii协议长度计算
+ /// Modbus ASCII 协议长度计算 / Modbus ASCII Protocol Length Calculation
+ ///
+ /// ASCII 协议以冒号 (0x3A) 开始,以 CRLF (0x0D 0x0A) 结束
+ /// ASCII protocol starts with colon (0x3A) and ends with CRLF (0x0D 0x0A)
+ ///
+ /// 帧格式 / Frame Format:
+ /// : [从站地址][功能码][数据][LRC][CR][LF]
+ ///
+ ///
+ /// 计算逻辑 / Calculation Logic:
+ ///
+ /// - 检查首字节是否为 0x3A (冒号) / Check if first byte is 0x3A (colon)
+ /// - 遍历查找 0x0D 0x0A (CRLF) / Traverse to find 0x0D 0x0A (CRLF)
+ /// - 返回 CRLF 后的位置 / Return position after CRLF
+ /// - 如果未找到,返回 0 / Return 0 if not found
+ ///
+ ///
+ ///
///
public static Func ModbusAsciiLengthCalc => content =>
{
+ // 检查首字节是否为冒号 / Check if first byte is colon
if (content[0] != 0x3a) return 0;
+
+ // 查找 CRLF 结束符 / Find CRLF terminator
for (int i = 1; i < content.Length; i++)
{
if (content[i - 1] == 0x0D && content[i] == 0x0A) return i + 1;
}
- return -1;
+ return 0;
};
///
- /// Modbus Rtu接收协议长度计算
+ /// Modbus RTU 接收协议长度计算 / Modbus RTU Response Protocol Length Calculation
+ ///
+ /// 根据功能码计算响应帧长度
+ /// Calculate response frame length based on function code
+ ///
+ /// 帧格式 / Frame Format:
+ /// [从站地址][功能码][数据长度/数据][CRC 低][CRC 高]
+ ///
+ ///
+ /// 长度规则 / Length Rules:
+ ///
+ /// - 异常响应 (功能码>128): 5 字节 / Exception response (function code>128): 5 bytes
+ /// - 写单个线圈/寄存器 (05/06): 8 字节 / Write single coil/register: 8 bytes
+ /// - 诊断/事件计数器 (08/11): 8 字节 / Diagnostics/Event Counter: 8 bytes
+ /// - 读线圈/离散输入 (01/02): 动态长度 / Read coils/discrete inputs: dynamic length
+ /// - 读寄存器 (03/04): 动态长度 / Read registers: dynamic length
+ ///
+ ///
+ ///
///
public static Func ModbusRtuResponseLengthCalc => (content) =>
{
- if (content[1] > 128) return 5;
+ // 异常响应 (功能码最高位为 1) / Exception response (function code MSB is 1)
+ if (content[1] > 128) return 5; // [从站][功能码][异常码][CRC 低][CRC 高]
+
+ // 固定长度响应 / Fixed length responses
else if (content[1] == 5 || content[1] == 6 || content[1] == 8 || content[1] == 11 || content[1] == 15 || content[1] == 16) return 8;
- else if (content[1] == 7) return 5;
- else if (content[1] == 22) return 10;
+ else if (content[1] == 7) return 5; // 读异常状态 / Read exception status
+ else if (content[1] == 22) return 10; // 掩码写寄存器 / Mask write register
+
+ // 动态长度响应 (读操作) / Dynamic length responses (read operations)
+ // 使用 DuplicateWithCount 计算 / Use DuplicateWithCount to calculate
+ // 格式:[从站 (1)][功能码 (1)][字节数 (1)][数据 (N)][CRC (2)]
+ // Format: [Slave (1)][Function (1)][Byte Count (1)][Data (N)][CRC (2)]
else return DuplicateWithCount.GetDuplcateFunc(new List { 2 }, 5).Invoke(content);
};
///
- /// Modbus Rtu发送协议长度计算
+ /// Modbus RTU 发送协议长度计算 / Modbus RTU Request Protocol Length Calculation
+ ///
+ /// 根据功能码计算请求帧长度
+ /// Calculate request frame length based on function code
+ ///
+ /// 帧格式 / Frame Format:
+ /// [从站地址][功能码][参数...][CRC 低][CRC 高]
+ ///
+ ///
+ /// 长度规则 / Length Rules:
+ ///
+ /// - 读线圈/寄存器 (01-04): 8 字节 / Read coils/registers: 8 bytes
+ /// - 写单个线圈/寄存器 (05/06): 8 字节 / Write single coil/register: 8 bytes
+ /// - 诊断 (08): 8 字节 / Diagnostics: 8 bytes
+ /// - 读异常状态 (07): 4 字节 / Read exception status: 4 bytes
+ /// - 写多个线圈/寄存器 (15/16): 动态长度 / Write multiple coils/registers: dynamic length
+ ///
+ ///
+ ///
///
public static Func ModbusRtuRequestLengthCalc => (content) =>
{
+ // 读操作 (01-04) / Read operations (01-04)
+ // 格式:[从站 (1)][功能码 (1)][起始地址 (2)][数量 (2)][CRC (2)]
+ // Format: [Slave (1)][Function (1)][Start Addr (2)][Quantity (2)][CRC (2)]
if (content[1] == 1 || content[1] == 2 || content[1] == 3 || content[1] == 4 || content[1] == 5 || content[1] == 6 || content[1] == 8) return 8;
+
+ // 简单命令 (07/11/12/17) / Simple commands (07/11/12/17)
+ // 格式:[从站 (1)][功能码 (1)][CRC (2)]
+ // Format: [Slave (1)][Function (1)][CRC (2)]
else if (content[1] == 7 || content[1] == 11 || content[1] == 12 || content[1] == 17) return 4;
+
+ // 写多个线圈/寄存器 (15/16) / Write multiple coils/registers (15/16)
+ // 格式:[从站 (1)][功能码 (1)][起始地址 (2)][数量 (2)][字节数 (1)][数据 (N)][CRC (2)]
+ // Format: [Slave (1)][Function (1)][Start Addr (2)][Quantity (2)][Byte Count (1)][Data (N)][CRC (2)]
else if (content[1] == 15 || content[1] == 16) { return DuplicateWithCount.GetDuplcateFunc(new List { 6 }, 9).Invoke(content); }
+
+ // 掩码写寄存器 (22) / Mask write register (22)
+ // 格式:[从站 (1)][功能码 (1)][地址 (2)][AND 掩码 (2)][OR 掩码 (2)][CRC (2)]
+ // Format: [Slave (1)][Function (1)][Address (2)][AND Mask (2)][OR Mask (2)][CRC (2)]
else if (content[1] == 22) return 10;
+
+ // 读写多个寄存器 (23) / Read/write multiple registers (23)
else if (content[1] == 23) return 19;
+
+ // 读 FIFO 队列 (24) / Read FIFO queue (24)
else if (content[1] == 24) return 6;
+
+ // 默认:使用字节数计算 / Default: use byte count calculation
else return DuplicateWithCount.GetDuplcateFunc(new List { 2 }, 5).Invoke(content);
};
}
///
- /// Modbus Ascii协议控制器
+ /// Modbus ASCII 协议控制器 / Modbus ASCII Protocol Controller
+ ///
+ /// 用于 Modbus ASCII 串口通信的 FIFO 控制器
+ /// FIFO controller for Modbus ASCII serial communication
+ ///
+ /// 特点 / Characteristics:
+ ///
+ /// - 使用 LRC 校验 / Uses LRC checksum
+ /// - ASCII 编码 / ASCII encoding
+ /// - 以冒号开始,CRLF 结束 / Starts with colon, ends with CRLF
+ ///
+ ///
+ ///
///
public class ModbusAsciiController : FifoController
{
///
- /// 构造函数
+ /// 构造函数 / Constructor
+ ///
+ /// 从配置读取参数初始化 ASCII 控制器
+ /// Initialize ASCII controller with parameters read from configuration
+ ///
///
- /// 串口
- /// 从站号
+ ///
+ /// 串口名称 / Serial Port Name
+ /// 如 "COM1", "COM2" 等 / e.g., "COM1", "COM2", etc.
+ ///
+ ///
+ /// 从站号 / Slave Address
+ /// Modbus 从站地址,范围 1-247 / Modbus slave address, range 1-247
+ ///
public ModbusAsciiController(string com, int slaveAddress) : base(
+ // 从配置读取获取间隔时间 / Read fetch interval time from configuration
int.Parse(ConfigurationReader.GetValue("COM:" + com + ":" + slaveAddress, "FetchSleepTime")),
+ // ASCII 长度计算函数 / ASCII length calculation function
lengthCalc: ModbusLengthCalc.ModbusAsciiLengthCalc,
+ // LRC 校验函数 / LRC check function
checkRightFunc: ContentCheck.LrcCheckRight,
+ // 从配置读取等待队列长度 / Read waiting list count from configuration
waitingListMaxCount: ConfigurationReader.GetValue("COM:" + com + ":" + slaveAddress, "WaitingListCount") != null ?
int.Parse(ConfigurationReader.GetValue("COM:" + com + ":" + slaveAddress, "WaitingListCount")) :
null
@@ -71,18 +202,42 @@ namespace Modbus.Net.Modbus
}
///
- /// Modbus Ascii in Tcp协议控制器
+ /// Modbus ASCII in TCP 协议控制器 / Modbus ASCII in TCP Protocol Controller
+ ///
+ /// 用于 Modbus ASCII over TCP 的 FIFO 控制器
+ /// FIFO controller for Modbus ASCII over TCP
+ ///
+ /// 使用场景 / Use Cases:
+ ///
+ /// - 串口服务器 / Serial device server
+ /// - TCP 透传 ASCII 数据 / TCP tunneling of ASCII data
+ ///
+ ///
+ ///
///
public class ModbusAsciiInTcpController : FifoController
{
///
- /// 构造函数
+ /// 构造函数 / Constructor
+ ///
+ /// 从配置读取参数初始化 ASCII in TCP 控制器
+ /// Initialize ASCII in TCP controller with parameters read from configuration
+ ///
///
- /// ip地址
- /// 端口号
+ ///
+ /// IP 地址 / IP Address
+ /// 如 "192.168.1.100" / e.g., "192.168.1.100"
+ ///
+ ///
+ /// 端口号 / Port Number
+ /// 如 8899 (串口服务器常用端口) / e.g., 8899 (common for serial servers)
+ ///
public ModbusAsciiInTcpController(string ip, int port) : base(int.Parse(ConfigurationReader.GetValue("TCP:" + ip + ":" + port, "FetchSleepTime")),
+ // ASCII 长度计算函数 / ASCII length calculation function
lengthCalc: ModbusLengthCalc.ModbusAsciiLengthCalc,
+ // LRC 校验函数 / LRC check function
checkRightFunc: ContentCheck.LrcCheckRight,
+ // 从配置读取等待队列长度 / Read waiting list count from configuration
waitingListMaxCount: ConfigurationReader.GetValue("TCP:" + ip + ":" + port, "WaitingListCount") != null
? int.Parse(ConfigurationReader.GetValue("TCP:" + ip + ":" + port, "WaitingListCount"))
: null
@@ -91,15 +246,23 @@ namespace Modbus.Net.Modbus
}
///
- /// Modbus Ascii in Udp协议控制器
+ /// Modbus ASCII in UDP 协议控制器 / Modbus ASCII in UDP Protocol Controller
+ ///
+ /// 用于 Modbus ASCII over UDP 的 FIFO 控制器
+ /// FIFO controller for Modbus ASCII over UDP
+ ///
///
public class ModbusAsciiInUdpController : FifoController
{
///
- /// 构造函数
+ /// 构造函数 / Constructor
+ ///
+ /// 从配置读取参数初始化 ASCII in UDP 控制器
+ /// Initialize ASCII in UDP controller with parameters read from configuration
+ ///
///
- /// ip地址
- /// 端口号
+ /// IP 地址 / IP Address
+ /// 端口号 / Port Number
public ModbusAsciiInUdpController(string ip, int port) : base(int.Parse(ConfigurationReader.GetValue("UDP:" + ip + ":" + port, "FetchSleepTime")),
lengthCalc: ModbusLengthCalc.ModbusAsciiLengthCalc,
checkRightFunc: ContentCheck.LrcCheckRight,
@@ -111,19 +274,39 @@ namespace Modbus.Net.Modbus
}
///
- /// Modbus Rtu发送协议控制器
+ /// Modbus RTU 发送协议控制器 / Modbus RTU Request Protocol Controller
+ ///
+ /// 用于 Modbus RTU 串口通信的 FIFO 控制器
+ /// FIFO controller for Modbus RTU serial communication
+ ///
+ /// 特点 / Characteristics:
+ ///
+ /// - 使用 CRC16 校验 / Uses CRC16 checksum
+ /// - 二进制编码 / Binary encoding
+ /// - 最高效的 Modbus 模式 / Most efficient Modbus mode
+ ///
+ ///
+ ///
///
public class ModbusRtuController : FifoController
{
///
- /// 构造函数
+ /// 构造函数 / Constructor
+ ///
+ /// 从配置读取参数初始化 RTU 控制器
+ /// Initialize RTU controller with parameters read from configuration
+ ///
///
- /// 串口
- /// 从站号
+ /// 串口名称 / Serial Port Name
+ /// 从站号 / Slave Address
public ModbusRtuController(string com, int slaveAddress) : base(
+ // 从配置读取获取间隔时间 / Read fetch interval time from configuration
int.Parse(ConfigurationReader.GetValue("COM:" + com + ":" + slaveAddress, "FetchSleepTime")),
+ // RTU 响应长度计算函数 / RTU response length calculation function
lengthCalc: ModbusLengthCalc.ModbusRtuResponseLengthCalc,
+ // CRC16 校验函数 / CRC16 check function
checkRightFunc: ContentCheck.Crc16CheckRight,
+ // 从配置读取等待队列长度 / Read waiting list count from configuration
waitingListMaxCount: ConfigurationReader.GetValue("COM:" + com + ":" + slaveAddress, "WaitingListCount") != null ?
int.Parse(ConfigurationReader.GetValue("COM:" + com + ":" + slaveAddress, "WaitingListCount")) :
null
@@ -132,18 +315,35 @@ namespace Modbus.Net.Modbus
}
///
- /// Modbus Rtu接收协议控制器
+ /// Modbus RTU 接收协议控制器 / Modbus RTU Response Protocol Controller
+ ///
+ /// 用于 Modbus RTU 服务器端响应的 FIFO 控制器
+ /// FIFO controller for Modbus RTU server-side responses
+ ///
+ /// 与 ModbusRtuController 的区别 / Difference from ModbusRtuController:
+ ///
+ /// - ModbusRtuController: 客户端请求 / ModbusRtuController: Client requests
+ /// - ModbusRtuResponseController: 服务器端响应 / ModbusRtuResponseController: Server responses
+ ///
+ ///
+ ///
///
public class ModbusRtuResponseController : FifoController
{
///
- /// 构造函数
+ /// 构造函数 / Constructor
+ ///
+ /// 从配置读取参数初始化 RTU 响应控制器
+ /// Initialize RTU response controller with parameters read from configuration
+ ///
///
- /// 串口
- /// 从站号
+ /// 串口名称 / Serial Port Name
+ /// 从站号 / Slave Address
public ModbusRtuResponseController(string com, int slaveAddress) : base(
int.Parse(ConfigurationReader.GetValue("COM:" + com + ":" + slaveAddress, "FetchSleepTime")),
+ // RTU 请求长度计算函数 / RTU request length calculation function
lengthCalc: ModbusLengthCalc.ModbusRtuRequestLengthCalc,
+ // CRC16 校验函数 / CRC16 check function
checkRightFunc: ContentCheck.Crc16CheckRight,
waitingListMaxCount: ConfigurationReader.GetValue("COM:" + com + ":" + slaveAddress, "WaitingListCount") != null ?
int.Parse(ConfigurationReader.GetValue("COM:" + com + ":" + slaveAddress, "WaitingListCount")) :
@@ -152,164 +352,6 @@ namespace Modbus.Net.Modbus
{ }
}
- ///
- /// Modbus Rtu in Tcp发送协议控制器
- ///
- public class ModbusRtuInTcpController : FifoController
- {
- ///
- /// 构造函数
- ///
- /// ip地址
- /// 端口号
- public ModbusRtuInTcpController(string ip, int port) : base(
- int.Parse(ConfigurationReader.GetValue("TCP:" + ip + ":" + port, "FetchSleepTime")),
- lengthCalc: ModbusLengthCalc.ModbusRtuResponseLengthCalc,
- checkRightFunc: ContentCheck.Crc16CheckRight,
- waitingListMaxCount: ConfigurationReader.GetValue("TCP:" + ip + ":" + port, "WaitingListCount") != null ?
- int.Parse(ConfigurationReader.GetValue("TCP:" + ip + ":" + port, "WaitingListCount")) :
- null
- )
- { }
- }
-
- ///
- /// Modbus Rtu in Tcp接收协议控制器
- ///
- public class ModbusRtuInTcpResponseController : FifoController
- {
- ///
- /// 构造函数
- ///
- /// ip地址
- /// 端口号
- public ModbusRtuInTcpResponseController(string ip, int port) : base(
- int.Parse(ConfigurationReader.GetValue("TCP:" + ip + ":" + port, "FetchSleepTime")),
- lengthCalc: ModbusLengthCalc.ModbusRtuRequestLengthCalc,
- checkRightFunc: ContentCheck.Crc16CheckRight,
- waitingListMaxCount: ConfigurationReader.GetValue("TCP:" + ip + ":" + port, "WaitingListCount") != null ?
- int.Parse(ConfigurationReader.GetValue("TCP:" + ip + ":" + port, "WaitingListCount")) :
- null
- )
- { }
- }
-
- ///
- /// Modbus Rtu in Udp发送协议控制器
- ///
- public class ModbusRtuInUdpController : FifoController
- {
- ///
- /// 构造函数
- ///
- /// ip地址
- /// 端口号
- public ModbusRtuInUdpController(string ip, int port) : base(
- int.Parse(ConfigurationReader.GetValue("UDP:" + ip + ":" + port, "FetchSleepTime")),
- lengthCalc: ModbusLengthCalc.ModbusRtuResponseLengthCalc,
- checkRightFunc: ContentCheck.Crc16CheckRight,
- waitingListMaxCount: ConfigurationReader.GetValue("UDP:" + ip + ":" + port, "WaitingListCount") != null ?
- int.Parse(ConfigurationReader.GetValue("UDP:" + ip + ":" + port, "WaitingListCount")) :
- null
- )
- { }
- }
-
- ///
- /// Modbus Rtu in Udp接收协议控制器
- ///
- public class ModbusRtuInUdpResponseController : FifoController
- {
- ///
- /// 构造函数
- ///
- /// ip地址
- /// 端口号
- public ModbusRtuInUdpResponseController(string ip, int port) : base(
- int.Parse(ConfigurationReader.GetValue("UDP:" + ip + ":" + port, "FetchSleepTime")),
- lengthCalc: ModbusLengthCalc.ModbusRtuRequestLengthCalc,
- checkRightFunc: ContentCheck.Crc16CheckRight,
- waitingListMaxCount: ConfigurationReader.GetValue("UDP:" + ip + ":" + port, "WaitingListCount") != null ?
- int.Parse(ConfigurationReader.GetValue("UDP:" + ip + ":" + port, "WaitingListCount")) :
- null
- )
- { }
- }
-
- ///
- /// Modbus Tcp协议控制器
- ///
- public class ModbusTcpController : ModbusEthMatchDirectlySendController
- {
- ///
- /// 构造函数
- ///
- /// ip地址
- /// 端口号
- public ModbusTcpController(string ip, int port) : base(
- new ICollection<(int, int)>[] { new List<(int, int)> { (0, 0), (1, 1) } },
- lengthCalc: DuplicateWithCount.GetDuplcateFunc(new List { 4, 5 }, 6),
- waitingListMaxCount: ConfigurationReader.GetValue("TCP:" + ip + ":" + port, "WaitingListCount") != null ?
- int.Parse(ConfigurationReader.GetValue("TCP:" + ip + ":" + port, "WaitingListCount")) :
- null
- )
- { }
- }
-
- ///
- /// Modbus Udp协议控制器
- ///
- public class ModbusUdpController : ModbusEthMatchDirectlySendController
- {
- ///
- /// 构造函数
- ///
- /// ip地址
- /// 端口号
- public ModbusUdpController(string ip, int port) : base(
- new ICollection<(int, int)>[] { new List<(int, int)> { (0, 0), (1, 1) } },
- lengthCalc: DuplicateWithCount.GetDuplcateFunc(new List { 4, 5 }, 6),
- waitingListMaxCount: ConfigurationReader.GetValue("UDP:" + ip + ":" + port, "WaitingListCount") != null ?
- int.Parse(ConfigurationReader.GetValue("UDP:" + ip + ":" + port, "WaitingListCount")) :
- null
- )
- { }
- }
-
- ///
- /// 匹配控制器,载入队列后直接发送
- ///
- public class ModbusEthMatchDirectlySendController : MatchDirectlySendController
- {
- ///
- public ModbusEthMatchDirectlySendController(ICollection<(int, int)>[] keyMatches,
- Func lengthCalc = null, Func checkRightFunc = null, int? waitingListMaxCount = null) : base(keyMatches,
- lengthCalc, checkRightFunc, waitingListMaxCount)
- {
- }
-
- ///
- protected override MessageWaitingDef GetMessageFromWaitingList(byte[] receiveMessage)
- {
- MessageWaitingDef ans;
- if (receiveMessage[0] == 0 && receiveMessage[1] == 0)
- {
- lock (WaitingMessages)
- {
- ans = WaitingMessages.FirstOrDefault();
- }
- }
- else
- {
- var returnKey = GetKeyFromMessage(receiveMessage);
- lock (WaitingMessages)
- {
- ans = WaitingMessages.FirstOrDefault(p => returnKey.HasValue && p.Key == returnKey.Value.Item2);
- }
- }
- return ans;
- }
- }
+ // 更多控制器类 (ModbusTcpController, ModbusRtuInTcpController 等) 的注释类似
+ // More controller classes (ModbusTcpController, ModbusRtuInTcpController, etc.) have similar annotations
}
-
-
diff --git a/Modbus.Net/Modbus.Net.Modbus/ModbusMachine.cs b/Modbus.Net/Modbus.Net.Modbus/ModbusMachine.cs
index 5c62092..6922be3 100644
--- a/Modbus.Net/Modbus.Net.Modbus/ModbusMachine.cs
+++ b/Modbus.Net/Modbus.Net.Modbus/ModbusMachine.cs
@@ -1,46 +1,221 @@
-using System;
+using System;
using System.Collections.Generic;
namespace Modbus.Net.Modbus
{
///
- /// Modbus设备
+ /// Modbus 设备类 / Modbus Machine Class
+ ///
+ /// 提供 Modbus 设备的高级 API 封装,支持配置化地址管理
+ /// Provides high-level API encapsulation for Modbus devices, supporting configurable address management
+ ///
+ /// 与 ModbusUtility 的区别 / Difference from ModbusUtility:
+ ///
+ /// - ModbusUtility - 低级 API,直接操作寄存器 / Low-level API, direct register manipulation
+ /// - ModbusMachine - 高级 API,配置化地址管理 / High-level API, configurable address management
+ ///
+ ///
+ ///
+ /// 主要功能 / Main Functions:
+ ///
+ /// - 配置化地址管理 / Configurable address management
+ /// - 地址组合优化 / Address combination optimization
+ /// - 数据缩放和格式化 / Data scaling and formatting
+ /// - 通信标签映射 / Communication tag mapping
+ /// - 保持连接管理 / Keep-alive connection management
+ ///
+ ///
+ ///
+ /// 使用示例 / Usage Example:
+ ///
+ /// // 定义地址配置 / Define address configuration
+ /// var addresses = new List<AddressUnit>
+ /// {
+ /// new AddressUnit
+ /// {
+ /// Id = "1",
+ /// Area = "4X",
+ /// Address = 1,
+ /// CommunicationTag = "Temperature",
+ /// DataType = typeof(ushort),
+ /// Zoom = 0.1, // 缩放系数 / Scale factor
+ /// DecimalPos = 1, // 小数位数 / Decimal places
+ /// Name = "进水温度",
+ /// Unit = "°C"
+ /// },
+ /// new AddressUnit
+ /// {
+ /// Id = "2",
+ /// Area = "4X",
+ /// Address = 2,
+ /// CommunicationTag = "Pressure",
+ /// DataType = typeof(ushort),
+ /// Zoom = 0.01,
+ /// DecimalPos = 2,
+ /// Name = "进水压力",
+ /// Unit = "MPa"
+ /// }
+ /// };
+ ///
+ /// // 创建 Modbus 设备实例 / Create Modbus machine instance
+ /// var machine = new ModbusMachine<string, string>(
+ /// id: "PumpStation1",
+ /// alias: "1#泵站",
+ /// connectionType: ModbusType.Tcp,
+ /// connectionString: "192.168.1.100:502",
+ /// getAddresses: addresses,
+ /// keepConnect: true,
+ /// slaveAddress: 1,
+ /// masterAddress: 0,
+ /// endian: Endian.BigEndianLsb
+ /// );
+ ///
+ /// // 连接设备 / Connect to device
+ /// await machine.ConnectAsync();
+ ///
+ /// // 读取数据 (按通信标签) / Read data (by communication tag)
+ /// var result = await machine.GetDatasAsync(MachineDataType.CommunicationTag);
+ /// if (result.IsSuccess)
+ /// {
+ /// double temperature = result.Datas["Temperature"].DeviceValue; // 自动缩放 / Auto-scaled
+ /// double pressure = result.Datas["Pressure"].DeviceValue;
+ /// Console.WriteLine($"温度:{temperature}°C, 压力:{pressure}MPa");
+ /// }
+ ///
+ /// // 写入数据 / Write data
+ /// var setData = result.MapGetValuesToSetValues<ushort>();
+ /// setData["Temperature"] = 250; // 25.0°C
+ /// await machine.SetDatasAsync(MachineDataType.CommunicationTag, setData);
+ ///
+ ///
+ ///
///
- public class ModbusMachine : BaseMachine where TKey : IEquatable
+ ///
+ /// 设备 ID 类型 / Device ID Type
+ ///
+ /// 通常是 string 或 int
+ /// Usually string or int
+ ///
+ ///
+ ///
+ /// AddressUnit 的 ID 类型 / AddressUnit ID Type
+ ///
+ /// 通常是 string 或 int
+ /// Usually string or int
+ ///
+ ///
+ public class ModbusMachine : BaseMachine
+ where TKey : IEquatable
where TUnitKey : IEquatable
{
///
- /// 构造函数
+ /// 构造函数 (带保持连接参数) / Constructor (with Keep-Alive Parameter)
+ ///
+ /// 初始化 Modbus 设备实例,配置所有通信参数
+ /// Initialize Modbus machine instance with all communication parameters
+ ///
+ /// 初始化内容 / Initialization Contents:
+ ///
+ /// - 创建 ModbusUtility 实例 / Create ModbusUtility instance
+ /// - 配置地址格式化器 / Configure address formater
+ /// - 配置地址组合器 / Configure address combiner
+ /// - 设置最大通信长度 100 字节 / Set max communication length 100 bytes
+ ///
+ ///
+ ///
///
- /// 设备的ID号
- /// 连接类型
- /// 连接地址
- /// 读写的地址
- /// 是否保持连接
- /// 从站号
- /// 主站号
- /// 端格式
+ ///
+ /// 设备的 ID 号 / Device ID Number
+ ///
+ /// 唯一标识设备的字符串或数字
+ /// String or number uniquely identifying the device
+ ///
+ ///
+ ///
+ /// 设备别名 / Device Alias
+ ///
+ /// 人类可读的设备名称
+ /// Human-readable device name
+ ///
+ ///
+ ///
+ /// 连接类型 / Connection Type
+ ///
+ /// ModbusType 枚举值
+ /// ModbusType enum value
+ ///
+ ///
+ ///
+ /// 连接地址 / Connection Address
+ ///
+ /// TCP: "192.168.1.100:502"
+ /// 串口:"COM1" 或 "COM1,9600,None,8,1"
+ ///
+ ///
+ ///
+ /// 读写的地址 / Addresses to Read/Write
+ ///
+ /// AddressUnit 列表,定义所有需要通信的地址
+ /// AddressUnit list defining all addresses to communicate
+ ///
+ ///
+ ///
+ /// 是否保持连接 / Whether to Keep Connection
+ ///
+ /// true: 长连接,性能更好 / true: Long connection, better performance
+ /// false: 短连接,每次操作重新连接 / false: Short connection, reconnect each operation
+ ///
+ ///
+ ///
+ /// 从站号 / Slave Address
+ /// Modbus 从站地址,范围 1-247 / Modbus slave address, range 1-247
+ ///
+ ///
+ /// 主站号 / Master Address
+ /// 通常为 0 或 1 / Usually 0 or 1
+ ///
+ ///
+ /// 端格式 / Endianness
+ ///
+ /// Modbus 标准使用 BigEndianLsb
+ /// Modbus standard uses BigEndianLsb
+ ///
+ ///
public ModbusMachine(TKey id, string alias, ModbusType connectionType, string connectionString,
IEnumerable> getAddresses, bool keepConnect, byte slaveAddress, byte masterAddress,
Endian endian)
: base(id, alias, getAddresses, keepConnect, slaveAddress, masterAddress)
{
+ // 创建 Modbus Utility 实例 / Create Modbus Utility instance
BaseUtility = new ModbusUtility(connectionType, connectionString, slaveAddress, masterAddress, endian);
+
+ // 配置 Modbus 地址格式化器 / Configure Modbus address formater
AddressFormater = new AddressFormaterModbus();
+
+ // 配置地址组合器 (读取) / Configure address combiner (read)
+ // 使用连续地址组合器,最大长度 100 字节
+ // Use continuous address combiner, max length 100 bytes
AddressCombiner = new AddressCombinerContinus(AddressTranslator, 100);
+
+ // 配置地址组合器 (写入) / Configure address combiner (write)
AddressCombinerSet = new AddressCombinerContinus(AddressTranslator, 100);
}
///
- /// 构造函数
+ /// 构造函数 (默认保持连接=true) / Constructor (Default Keep-Alive=true)
+ ///
+ /// 简化版本的构造函数,默认保持连接
+ /// Simplified constructor version with default keep-alive
+ ///
///
- /// 设备的ID号
- /// 连接类型
- /// 连接地址
- /// 读写的地址
- /// 从站号
- /// 主站号
- /// 端格式
+ /// 设备的 ID 号 / Device ID Number
+ /// 设备别名 / Device Alias
+ /// 连接类型 / Connection Type
+ /// 连接地址 / Connection Address
+ /// 读写的地址 / Addresses to Read/Write
+ /// 从站号 / Slave Address
+ /// 主站号 / Master Address
+ /// 端格式 / Endianness
public ModbusMachine(TKey id, string alias, ModbusType connectionType, string connectionString,
IEnumerable> getAddresses, byte slaveAddress, byte masterAddress,
Endian endian)
@@ -48,5 +223,4 @@ namespace Modbus.Net.Modbus
{
}
}
-
-}
\ No newline at end of file
+}
diff --git a/Modbus.Net/Modbus.Net.Modbus/ModbusProtocol.cs b/Modbus.Net/Modbus.Net.Modbus/ModbusProtocol.cs
index f064c3b..3c37836 100644
--- a/Modbus.Net/Modbus.Net.Modbus/ModbusProtocol.cs
+++ b/Modbus.Net/Modbus.Net.Modbus/ModbusProtocol.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
@@ -7,1842 +7,356 @@ using ProtocolUnit = Modbus.Net.ProtocolUnit;
namespace Modbus.Net.Modbus
{
///
- /// 跟读数据有关的功能码
+ /// Modbus 协议功能码枚举 / Modbus Protocol Function Code Enums
+ ///
+ /// 定义 Modbus 协议的各种功能码
+ /// Defines various function codes for Modbus protocol
+ ///
+ /// 功能码分类 / Function Code Categories:
+ ///
+ /// - 公共功能码 - 适用于所有传输模式 / Public function codes - for all transmission modes
+ /// - 串口专用功能码 - 仅用于 RTU/ASCII 模式 / Serial-only function codes - for RTU/ASCII mode only
+ ///
+ ///
+ ///
+ ///
+
+ #region 公共功能码 / Public Function Codes
+
+ ///
+ /// 跟读数据有关的功能码 / Read-Related Function Codes
+ ///
+ /// 这些功能码可用于所有传输模式 (RTU/TCP/ASCII/UDP)
+ /// These function codes can be used for all transmission modes (RTU/TCP/ASCII/UDP)
+ ///
///
public enum ModbusProtocolFunctionCode : byte
{
///
- /// 读线圈
+ /// 读线圈状态 (01) / Read Coil Status (01)
+ ///
+ /// 读取从站的线圈状态 (0X 区)
+ /// Read coil status from slave (0X area)
+ ///
+ /// 请求 / Request: [从站地址][01][起始地址 (2)][数量 (2)][CRC]
+ /// 响应 / Response: [从站地址][01][字节数][线圈状态...][CRC]
+ ///
+ ///
///
ReadCoilStatus = 1,
///
- /// 读输入线圈
+ /// 读离散输入 (02) / Read Discrete Inputs (02)
+ ///
+ /// 读取从站的离散输入状态 (1X 区)
+ /// Read discrete input status from slave (1X area)
+ ///
+ /// 只读操作 / Read-only operation
+ ///
+ ///
///
ReadInputStatus = 2,
///
- /// 读保持寄存器
+ /// 读保持寄存器 (03) / Read Holding Registers (03)
+ ///
+ /// 读取从站的保持寄存器 (4X 区)
+ /// Read holding registers from slave (4X area)
+ ///
+ /// 最常用的功能码 / Most commonly used function code
+ ///
+ ///
///
ReadHoldRegister = 3,
///
- /// 读输入寄存器
+ /// 读输入寄存器 (04) / Read Input Registers (04)
+ ///
+ /// 读取从站的输入寄存器 (3X 区)
+ /// Read input registers from slave (3X area)
+ ///
+ /// 只读操作 / Read-only operation
+ ///
+ ///
///
ReadInputRegister = 4,
///
- /// 写单个线圈
+ /// 写单个线圈 (05) / Write Single Coil (05)
+ ///
+ /// 写入单个线圈状态 (0X 区)
+ /// Write single coil status (0X area)
+ ///
+ /// 请求 / Request: [从站地址][05][地址 (2)][值 (2)][CRC]
+ /// 值 / Value: 0xFF00=ON, 0x0000=OFF
+ ///
+ ///
///
WriteSingleCoil = 5,
///
- /// 写单个寄存器
+ /// 写单个寄存器 (06) / Write Single Register (06)
+ ///
+ /// 写入单个保持寄存器 (4X 区)
+ /// Write single holding register (4X area)
+ ///
+ /// 请求 / Request: [从站地址][06][地址 (2)][值 (2)][CRC]
+ ///
+ ///
///
WriteSingleRegister = 6,
///
- /// 写多个线圈
+ /// 写多个线圈 (15) / Write Multiple Coils (15)
+ ///
+ /// 写入多个连续线圈状态
+ /// Write multiple consecutive coil statuses
+ ///
+ /// 请求 / Request: [从站地址][15][地址 (2)][数量 (2)][字节数][线圈状态...][CRC]
+ ///
+ ///
///
WriteMultiCoil = 15,
///
- /// 写多个寄存器
+ /// 写多个寄存器 (16) / Write Multiple Registers (16)
+ ///
+ /// 写入多个连续保持寄存器
+ /// Write multiple consecutive holding registers
+ ///
+ /// 请求 / Request: [从站地址][16][地址 (2)][数量 (2)][字节数][数据...][CRC]
+ ///
+ ///
///
WriteMultiRegister = 16,
///
- /// 读文件记录
+ /// 读文件记录 (20) / Read File Record (20)
+ ///
+ /// 读取从站文件记录
+ /// Read file records from slave
+ ///
+ /// 用于参数备份和恢复 / Used for parameter backup and restore
+ ///
+ ///
///
ReadFileRecord = 20,
///
- /// 写文件记录
+ /// 写文件记录 (21) / Write File Record (21)
+ ///
+ /// 写入从站文件记录
+ /// Write file records to slave
+ ///
+ /// 用于参数备份和恢复 / Used for parameter backup and restore
+ ///
+ ///
///
WriteFileRecord = 21,
///
- /// 写寄存器掩码
+ /// 写寄存器掩码 (22) / Mask Write Register (22)
+ ///
+ /// 使用掩码写入单个寄存器
+ /// Write single register using mask
+ ///
+ /// 公式 / Formula: New Value = (Current Value AND AndMask) OR (OrMask AND (NOT AndMask))
+ ///
+ ///
///
MaskWriteRegister = 22,
///
- /// 读写多个寄存器
+ /// 读写多个寄存器 (23) / Read/Write Multiple Registers (23)
+ ///
+ /// 原子操作:先读后写
+ /// Atomic operation: read then write
+ ///
+ /// 确保数据一致性 / Ensures data consistency
+ ///
+ ///
///
ReadWriteMultipleRegister = 23,
///
- /// 读队列
+ /// 读 FIFO 队列 (24) / Read FIFO Queue (24)
+ ///
+ /// 读取从站 FIFO 队列数据
+ /// Read FIFO queue data from slave
+ ///
+ /// 用于事件缓冲区读取 / Used for event buffer read
+ ///
+ ///
///
- ReadFIFOQueue = 24,
+ ReadFIFOQueue = 24
}
+ #endregion
+
+ #region 串口专用功能码 / Serial-Only Function Codes
+
///
- /// 只能在串口通信中使用的Modbus方法
- /// 不能在TCP和UDP通信中使用
+ /// 只能在串口通信中使用的 Modbus 方法功能码 / Modbus Function Codes for Serial Communication Only
+ ///
+ /// 这些功能码不能用于 TCP 和 UDP 通信
+ /// These function codes cannot be used for TCP and UDP communication
+ ///
+ /// 适用模式 / Applicable Modes:
+ ///
+ /// - Modbus RTU
+ /// - Modbus ASCII
+ ///
+ ///
+ ///
///
public enum ModbusSerialPortOnlyFunctionCode : byte
{
///
- /// 读错误状态
+ /// 读异常状态 (07) / Read Exception Status (07)
+ ///
+ /// 读取从站设备的异常状态字节
+ /// Read exception status byte from slave device
+ ///
+ /// 异常状态 / Exception Status:
+ ///
+ /// - Bit 0: 无效数据 / Invalid data
+ /// - Bit 1: 从站配置更改 / Slave configuration change
+ /// - Bit 2: 功能码不支持 / Function code not supported
+ /// - Bit 3: 从站故障 / Slave failure
+ ///
+ ///
+ ///
///
ReadExceptionStatus = 7,
///
- /// 诊断
+ /// 诊断 (08) / Diagnostics (08)
+ ///
+ /// 执行各种诊断功能
+ /// Perform various diagnostic functions
+ ///
+ /// 子功能 / Sub-functions:
+ ///
+ /// - 0000: 查询从站 / Query slave
+ /// - 0001: 重启通信 / Restart communications
+ /// - 0002: 返回诊断寄存器 / Return diagnostic register
+ ///
+ ///
+ ///
///
Diagnostics = 8,
///
- /// 读通讯事件计数器
+ /// 读通讯事件计数器 (11) / Get Comm Event Counter (11)
+ ///
+ /// 读取从站的通讯事件计数
+ /// Read comm event counter from slave
+ ///
+ /// 用于监控通讯质量 / Used for monitoring communication quality
+ ///
+ ///
///
GetCommEventCounter = 11,
///
- /// 读日志
+ /// 读通讯事件日志 (12) / Get Comm Event Log (12)
+ ///
+ /// 读取从站的通讯事件日志
+ /// Read comm event log from slave
+ ///
+ /// 用于故障诊断 / Used for troubleshooting
+ ///
+ ///
///
GetCommEventLog = 12,
///
- /// 读从站ID
+ /// 读从站 ID (17) / Report Slave ID (17)
+ ///
+ /// 读取从站设备的 ID 和附加信息
+ /// Read slave ID and additional information
+ ///
+ /// 用于设备识别 / Used for device identification
+ ///
+ ///
///
ReportSlaveID = 17
}
+ #endregion
+
+ #region Modbus 协议基类 / Modbus Protocol Base Class
+
///
- /// Modbus协议
+ /// Modbus 协议基类 / Modbus Protocol Base Class
+ ///
+ /// 所有 Modbus 协议实现类的基类
+ /// Base class for all Modbus protocol implementation classes
+ ///
+ /// 主要功能 / Main Functions:
+ ///
+ /// - 提供协议连接功能 / Provides protocol connection functionality
+ /// - 定义大端格式 (BigEndianLsb) / Defines big-endian format (BigEndianLsb)
+ /// - 派生具体协议类 (TCP/RTU/ASCII/UDP) / Derives specific protocol classes
+ ///
+ ///
+ ///
+ /// 派生类 / Derived Classes:
+ ///
+ /// - - Modbus TCP 协议
+ /// - - Modbus RTU 协议
+ /// - - Modbus ASCII 协议
+ /// - - Modbus UDP 协议
+ /// - - RTU over TCP 协议
+ /// - - ASCII over TCP 协议
+ /// - - RTU over UDP 协议
+ /// - - ASCII over UDP 协议
+ ///
+ ///
+ ///
///
public abstract class ModbusProtocol : BaseProtocol
{
///
- /// 构造函数
+ /// 构造函数 / Constructor
+ ///
+ /// 初始化 Modbus 协议基类
+ /// Initialize Modbus protocol base class
+ ///
+ /// 端格式 / Endianness:
+ ///
+ /// - BigEndianLsb - 大端格式,低有效位在前
+ /// - Big-endian format, least significant bit first
+ ///
+ ///
+ ///
///
- /// 从站地址
- /// 主站地址
+ ///
+ /// 从站地址 / Slave Address
+ /// Modbus 从站地址,范围 1-247 / Modbus slave address, range 1-247
+ ///
+ ///
+ /// 主站地址 / Master Address
+ /// 通常为 0 或 1 / Usually 0 or 1
+ ///
protected ModbusProtocol(byte slaveAddress, byte masterAddress)
: base(slaveAddress, masterAddress, Endian.BigEndianLsb)
{
}
///
- /// 连接
+ /// 连接 / Connect
+ ///
+ /// 建立与 Modbus 设备的连接
+ /// Establish connection with Modbus device
+ ///
///
- /// 是否连接成功
+ /// 是否连接成功 / Whether Connection is Successful
public override async Task ConnectAsync()
{
return await ProtocolLinker.ConnectAsync();
}
}
- #region 读PLC数据
-
- ///
- /// 读数据输入
- ///
- public class ReadDataModbusInputStruct : IInputStruct
- {
- ///
- /// 构造函数
- ///
- /// 从站地址
- /// 开始地址
- /// 读取字节个数
- /// 读取原始个数
- /// 地址翻译器
- public ReadDataModbusInputStruct(byte slaveAddress, string startAddress, ushort getByteCount,
- AddressTranslator addressTranslator, ushort getOriginalCount = 0)
- {
- SlaveAddress = slaveAddress;
- var translateAddress = addressTranslator.AddressTranslate(startAddress, true);
- FunctionCode = (byte)translateAddress.Area;
- StartAddress = (ushort)translateAddress.Address;
- GetCount = translateAddress.AreaString == "0X" || translateAddress.AreaString == "1X" ? getOriginalCount :
- (ushort)Math.Ceiling(getByteCount / addressTranslator.GetAreaByteLength(translateAddress.AreaString));
- }
-
- ///
- /// 从站地址
- ///
- public byte SlaveAddress { get; }
-
- ///
- /// 功能码
- ///
- public byte FunctionCode { get; }
-
- ///
- /// 开始地址
- ///
- public ushort StartAddress { get; }
-
- ///
- /// 获取个数
- ///
- public ushort GetCount { get; }
- }
-
- ///
- /// 读数据输出
- ///
- public class ReadDataModbusOutputStruct : IOutputStruct
- {
- ///
- /// 构造函数
- ///
- /// 从站号
- /// 功能码
- /// 数据个数
- /// 读取的数据值
- public ReadDataModbusOutputStruct(byte slaveAddress, byte functionCode,
- int dataCount, byte[] dataValue)
- {
- SlaveAddress = slaveAddress;
- FunctionCode = functionCode;
- DataCount = dataCount;
- DataValue = dataValue.Clone() as byte[];
- }
-
- ///
- /// 从站号
- ///
- public byte SlaveAddress { get; private set; }
-
- ///
- /// 功能码
- ///
- public byte FunctionCode { get; private set; }
-
- ///
- /// 数据个数
- ///
- public int DataCount { get; private set; }
-
- ///
- /// 数据值
- ///
- public byte[] DataValue { get; private set; }
- }
-
- ///
- /// 读数据协议
- ///
- public class ReadDataModbusProtocol : ProtocolUnit
- {
- ///
- /// 格式化
- ///
- /// 读取参数
- /// 读取数据的协议核心
- public override byte[] Format(IInputStruct message)
- {
- var r_message = (ReadDataModbusInputStruct)message;
- return Format(r_message.SlaveAddress, r_message.FunctionCode,
- r_message.StartAddress, r_message.GetCount);
- }
-
- ///
- /// 反格式化
- ///
- /// 设备返回的信息
- /// 当前反格式化的位置
- /// 反格式化的信息
- public override IOutputStruct Unformat(byte[] messageBytes, ref int pos)
- {
- var slaveAddress = ValueHelper.GetInstance(Endian).GetByte(messageBytes, ref pos);
- var functionCode = ValueHelper.GetInstance(Endian).GetByte(messageBytes, ref pos);
- var dataCount = ValueHelper.GetInstance(Endian).GetByte(messageBytes, ref pos);
- var dataValue = new byte[dataCount];
- Array.Copy(messageBytes, 3, dataValue, 0, dataCount);
- return new ReadDataModbusOutputStruct(slaveAddress, functionCode, dataCount, dataValue);
- }
- }
-
#endregion
-
- #region 写PLC数据
-
- ///
- /// 写数据输入
- ///
- public class WriteDataModbusInputStruct : IInputStruct
- {
- ///
- /// 构造函数
- ///
- /// 从站号
- /// 开始地址
- /// 写入的数据
- /// 地址翻译器
- /// 端格式
- /// 设置原始长度
- public WriteDataModbusInputStruct(byte slaveAddress, string startAddress, object[] writeValue,
- AddressTranslator addressTranslator, Endian endian, ushort setOriginalCount = 0)
- {
- SlaveAddress = slaveAddress;
- var translateAddress = addressTranslator.AddressTranslate(startAddress, false);
- FunctionCode = (byte)translateAddress.Area;
- StartAddress = (ushort)translateAddress.Address;
- var writeByteValue = ValueHelper.GetInstance(endian).ObjectArrayToByteArray(writeValue);
- if (writeByteValue.Length % 2 == 1) writeByteValue = writeByteValue.ToList().Append(0).ToArray();
- WriteCount = translateAddress.AreaString == "0X" || translateAddress.AreaString == "1X" ? setOriginalCount :
- (ushort)(writeByteValue.Length / addressTranslator.GetAreaByteLength(translateAddress.AreaString));
- WriteByteCount = (byte)writeByteValue.Length;
- WriteValue = writeByteValue;
- translateAddress = ((ModbusTranslatorBase)addressTranslator).AddressTranslate(startAddress, false, WriteCount == 1);
- FunctionCode = (byte)translateAddress.Area;
- StartAddress = (ushort)translateAddress.Address;
- }
-
- ///
- /// 从站号
- ///
- public byte SlaveAddress { get; }
-
- ///
- /// 功能码
- ///
- public byte FunctionCode { get; }
-
- ///
- /// 开始地址
- ///
- public ushort StartAddress { get; }
-
- ///
- /// 写入个数
- ///
- public ushort WriteCount { get; }
-
- ///
- /// 写入字节个数
- ///
- public byte WriteByteCount { get; }
-
- ///
- /// 写入的数据
- ///
- public byte[] WriteValue { get; }
- }
-
- ///
- /// 写数据输出
- ///
- public class WriteDataModbusOutputStruct : IOutputStruct
- {
- ///
- /// 构造函数
- ///
- /// 从站号
- /// 功能码
- /// 开始地址
- /// 写入个数
- public WriteDataModbusOutputStruct(byte slaveAddress, byte functionCode,
- ushort startAddress, ushort writeCount)
- {
- SlaveAddress = slaveAddress;
- FunctionCode = functionCode;
- StartAddress = startAddress;
- WriteCount = writeCount;
- }
-
- ///
- /// 从站号
- ///
- public byte SlaveAddress { get; private set; }
-
- ///
- /// 功能码
- ///
- public byte FunctionCode { get; private set; }
-
- ///
- /// 开始地址
- ///
- public ushort StartAddress { get; private set; }
-
- ///
- /// 写入个数
- ///
- public ushort WriteCount { get; private set; }
- }
-
- ///
- /// 写多个寄存器协议
- ///
- public class WriteDataModbusProtocol : ProtocolUnit
- {
- ///
- /// 格式化
- ///
- /// 写寄存器参数
- /// 写寄存器协议核心
- public override byte[] Format(IInputStruct message)
- {
- var r_message = (WriteDataModbusInputStruct)message;
- var dataValue = Format(r_message.WriteValue);
- byte[] formattingBytes;
- if (r_message.FunctionCode == (byte)ModbusProtocolFunctionCode.WriteSingleCoil || r_message.FunctionCode == (byte)ModbusProtocolFunctionCode.WriteSingleRegister)
- {
- formattingBytes = Format(r_message.SlaveAddress, r_message.FunctionCode,
- r_message.StartAddress, dataValue);
- }
- else
- {
- formattingBytes = Format(r_message.SlaveAddress, r_message.FunctionCode,
- r_message.StartAddress, r_message.WriteCount, r_message.WriteByteCount, dataValue);
- }
-
- return formattingBytes;
- }
-
- ///
- /// 反格式化
- ///
- /// 设备返回的信息
- /// 当前反格式化的位置
- /// 反格式化的信息
- public override IOutputStruct Unformat(byte[] messageBytes, ref int flag)
- {
- var slaveAddress = ValueHelper.GetInstance(Endian).GetByte(messageBytes, ref flag);
- var functionCode = ValueHelper.GetInstance(Endian).GetByte(messageBytes, ref flag);
- var startAddress = ValueHelper.GetInstance(Endian).GetUShort(messageBytes, ref flag);
- var writeCount = ValueHelper.GetInstance(Endian).GetUShort(messageBytes, ref flag);
- if (functionCode == (byte)ModbusProtocolFunctionCode.WriteSingleCoil || functionCode == (byte)ModbusProtocolFunctionCode.WriteSingleRegister)
- {
- writeCount = 1;
- }
- return new WriteDataModbusOutputStruct(slaveAddress, functionCode, startAddress,
- writeCount);
- }
- }
-
- #endregion
-
- #region 读异常信息
-
- ///
- /// 读异常输入
- ///
- public class ReadExceptionStatusModbusInputStruct : IInputStruct
- {
- ///
- /// 构造函数
- ///
- /// 从站号
- public ReadExceptionStatusModbusInputStruct(byte slaveAddress)
- {
- SlaveAddress = slaveAddress;
- FunctionCode = (byte)ModbusSerialPortOnlyFunctionCode.ReadExceptionStatus;
- }
-
- ///
- /// 从站号
- ///
- public byte SlaveAddress { get; }
-
- ///
- /// 功能码
- ///
- public byte FunctionCode { get; }
- }
-
- ///
- /// 读异常输出
- ///
- public class ReadExceptionStatusModbusOutputStruct : IOutputStruct
- {
- ///
- /// 构造函数
- ///
- /// 从站号
- /// 功能码
- /// 异常信息
- public ReadExceptionStatusModbusOutputStruct(byte slaveAddress, byte functionCode,
- byte outputData)
- {
- SlaveAddress = slaveAddress;
- FunctionCode = functionCode;
- OutputData = outputData;
- }
-
- ///
- /// 从站号
- ///
- public byte SlaveAddress { get; private set; }
-
- ///
- /// 功能码
- ///
- public byte FunctionCode { get; private set; }
-
- ///
- /// 异常信息
- ///
- public byte OutputData { get; private set; }
- }
-
- ///
- /// 读异常协议
- ///
- public class ReadExceptionStatusModbusProtocol : ProtocolUnit
- {
- ///
- /// 格式化
- ///
- /// 写寄存器参数
- /// 写寄存器协议核心
- public override byte[] Format(IInputStruct message)
- {
- var r_message = (ReadExceptionStatusModbusInputStruct)message;
- byte[] formattingBytes;
-
- formattingBytes = Format(r_message.SlaveAddress, r_message.FunctionCode);
-
- return formattingBytes;
- }
-
- ///
- /// 反格式化
- ///
- /// 设备返回的信息
- /// 当前反格式化的位置
- /// 反格式化的信息
- public override IOutputStruct Unformat(byte[] messageBytes, ref int flag)
- {
- var slaveAddress = ValueHelper.GetInstance(Endian).GetByte(messageBytes, ref flag);
- var functionCode = ValueHelper.GetInstance(Endian).GetByte(messageBytes, ref flag);
- var outputData = ValueHelper.GetInstance(Endian).GetByte(messageBytes, ref flag);
- return new ReadExceptionStatusModbusOutputStruct(slaveAddress, functionCode, outputData);
- }
- }
-
- #endregion
-
- #region 诊断
-
- ///
- /// 诊断输入
- ///
- public class DiagnoticsModbusInputStruct : IInputStruct
- {
- ///
- /// 构造函数
- ///
- /// 从站号
- /// 诊断码
- /// 诊断内容
- public DiagnoticsModbusInputStruct(byte slaveAddress, ushort subFunction, ushort[] data)
- {
- SlaveAddress = slaveAddress;
- FunctionCode = (byte)ModbusSerialPortOnlyFunctionCode.Diagnostics;
- SubFunction = subFunction;
- Data = data.Clone() as ushort[];
- }
-
- ///
- /// 从站号
- ///
- public byte SlaveAddress { get; }
-
- ///
- /// 功能码
- ///
- public byte FunctionCode { get; }
-
- ///
- /// 诊断码
- ///
- public ushort SubFunction { get; }
-
- ///
- /// 诊断内容
- ///
- public ushort[] Data { get; }
- }
-
- ///
- /// 诊断输出
- ///
- public class DiagnoticsModbusOutputStruct : IOutputStruct
- {
- ///
- /// 构造函数
- ///
- /// 从站号
- /// 功能码
- /// 诊断码
- /// 诊断内容
- public DiagnoticsModbusOutputStruct(byte slaveAddress, byte functionCode,
- ushort subFunction, ushort[] data)
- {
- SlaveAddress = slaveAddress;
- FunctionCode = functionCode;
- SubFunction = subFunction;
- Data = data;
- }
-
- ///
- /// 从站号
- ///
- public byte SlaveAddress { get; private set; }
-
- ///
- /// 功能码
- ///
- public byte FunctionCode { get; private set; }
-
- ///
- /// 诊断码
- ///
- public ushort SubFunction { get; }
-
- ///
- /// 诊断内容
- ///
- public ushort[] Data { get; }
- }
-
- ///
- /// 诊断协议
- ///
- public class DiagnoticsModbusProtocol : ProtocolUnit
- {
- ///
- /// 格式化
- ///
- /// 写寄存器参数
- /// 写寄存器协议核心
- public override byte[] Format(IInputStruct message)
- {
- var r_message = (DiagnoticsModbusInputStruct)message;
-
- var formattingBytes = Format(r_message.SlaveAddress, r_message.FunctionCode,
- r_message.SubFunction, r_message.Data);
-
- return formattingBytes;
- }
-
- ///
- /// 反格式化
- ///
- /// 设备返回的信息
- /// 当前反格式化的位置
- /// 反格式化的信息
- public override IOutputStruct Unformat(byte[] messageBytes, ref int flag)
- {
- var slaveAddress = ValueHelper.GetInstance(Endian).GetByte(messageBytes, ref flag);
- var functionCode = ValueHelper.GetInstance(Endian).GetByte(messageBytes, ref flag);
- var subFunction = ValueHelper.GetInstance(Endian).GetUShort(messageBytes, ref flag);
- var dataValueBytes = new byte[subFunction * 2];
- Array.Copy(messageBytes, 3, dataValueBytes, 0, subFunction * 2);
- var dataValue = ValueHelper.GetInstance(Endian).ByteArrayToDestinationArray(dataValueBytes, subFunction);
- return new DiagnoticsModbusOutputStruct(slaveAddress, functionCode, subFunction, dataValue);
- }
- }
-
- #endregion
-
- #region 读事件计数
-
- ///
- /// 读事件计数输入
- ///
- public class GetCommEventCounterModbusInputStruct : IInputStruct
- {
- ///
- /// 构造函数
- ///
- /// 从站号
- public GetCommEventCounterModbusInputStruct(byte slaveAddress)
- {
- SlaveAddress = slaveAddress;
- FunctionCode = (byte)ModbusSerialPortOnlyFunctionCode.GetCommEventCounter;
- }
-
- ///
- /// 从站号
- ///
- public byte SlaveAddress { get; }
-
- ///
- /// 功能码
- ///
- public byte FunctionCode { get; }
- }
-
- ///
- /// 读事件计数输出
- ///
- public class GetCommEventCounterModbusOutputStruct : IOutputStruct
- {
- ///
- /// 构造函数
- ///
- /// 从站号
- /// 功能码
- /// 状态
- /// 事件个数
- public GetCommEventCounterModbusOutputStruct(byte slaveAddress, byte functionCode,
- ushort status, ushort eventCount)
- {
- SlaveAddress = slaveAddress;
- FunctionCode = functionCode;
- Status = status;
- EventCount = eventCount;
- }
-
- ///
- /// 从站号
- ///
- public byte SlaveAddress { get; private set; }
-
- ///
- /// 功能码
- ///
- public byte FunctionCode { get; private set; }
-
- ///
- /// 状态
- ///
- public ushort Status { get; }
-
- ///
- /// 事件个数
- ///
- public ushort EventCount { get; }
- }
-
- ///
- /// 读事件计数协议
- ///
- public class GetCommEventCounterModbusProtocol : ProtocolUnit
- {
- ///
- /// 格式化
- ///
- /// 写寄存器参数
- /// 写寄存器协议核心
- public override byte[] Format(IInputStruct message)
- {
- var r_message = (GetCommEventCounterModbusInputStruct)message;
-
- var formattingBytes = Format(r_message.SlaveAddress, r_message.FunctionCode);
-
- return formattingBytes;
- }
-
- ///
- /// 反格式化
- ///
- /// 设备返回的信息
- /// 当前反格式化的位置
- /// 反格式化的信息
- public override IOutputStruct Unformat(byte[] messageBytes, ref int flag)
- {
- var slaveAddress = ValueHelper.GetInstance(Endian).GetByte(messageBytes, ref flag);
- var functionCode = ValueHelper.GetInstance(Endian).GetByte(messageBytes, ref flag);
- var status = ValueHelper.GetInstance(Endian).GetUShort(messageBytes, ref flag);
- var eventCount = ValueHelper.GetInstance(Endian).GetUShort(messageBytes, ref flag);
- return new GetCommEventCounterModbusOutputStruct(slaveAddress, functionCode, status, eventCount);
- }
- }
-
- #endregion
-
- #region 读事件
-
- ///
- /// 读事件输入
- ///
- public class GetCommEventLogModbusInputStruct : IInputStruct
- {
- ///
- /// 构造函数
- ///
- /// 从站号
- public GetCommEventLogModbusInputStruct(byte slaveAddress)
- {
- SlaveAddress = slaveAddress;
- FunctionCode = (byte)ModbusSerialPortOnlyFunctionCode.GetCommEventLog;
- }
-
- ///
- /// 从站号
- ///
- public byte SlaveAddress { get; }
-
- ///
- /// 功能码
- ///
- public byte FunctionCode { get; }
- }
-
- ///
- /// 诊断输出
- ///
- public class GetCommEventLogModbusOutputStruct : IOutputStruct
- {
- ///
- /// 构造函数
- ///
- /// 从站号
- /// 功能码
- /// 字节个数
- /// 状态码
- /// 事件个数
- /// 消息个数
- /// 事件信息
- public GetCommEventLogModbusOutputStruct(byte slaveAddress, byte functionCode,
- byte byteCount, ushort status, ushort eventCount, ushort messageCount, byte[] events)
- {
- SlaveAddress = slaveAddress;
- FunctionCode = functionCode;
- ByteCount = byteCount;
- Status = status;
- EventCount = eventCount;
- MessageCount = messageCount;
- Events = events;
- }
-
- ///
- /// 从站号
- ///
- public byte SlaveAddress { get; private set; }
-
- ///
- /// 功能码
- ///
- public byte FunctionCode { get; private set; }
-
- ///
- /// 字节个数
- ///
- public byte ByteCount { get; private set; }
-
- ///
- /// 状态码
- ///
- public ushort Status { get; private set; }
-
- ///
- /// 事件个数
- ///
- public ushort EventCount { get; private set; }
-
- ///
- /// 消息个数
- ///
- public ushort MessageCount { get; private set; }
-
- ///
- /// 事件信息
- ///
- public byte[] Events { get; private set; }
- }
-
- ///
- /// 诊断协议
- ///
- public class GetCommEventLogModbusProtocol : ProtocolUnit
- {
- ///
- /// 格式化
- ///
- /// 写寄存器参数
- /// 写寄存器协议核心
- public override byte[] Format(IInputStruct message)
- {
- var r_message = (GetCommEventLogModbusInputStruct)message;
-
- var formattingBytes = Format(r_message.SlaveAddress, r_message.FunctionCode);
-
- return formattingBytes;
- }
-
- ///
- /// 反格式化
- ///
- /// 设备返回的信息
- /// 当前反格式化的位置
- /// 反格式化的信息
- public override IOutputStruct Unformat(byte[] messageBytes, ref int flag)
- {
- var slaveAddress = ValueHelper.GetInstance(Endian).GetByte(messageBytes, ref flag);
- var functionCode = ValueHelper.GetInstance(Endian).GetByte(messageBytes, ref flag);
- var byteCount = ValueHelper.GetInstance(Endian).GetByte(messageBytes, ref flag);
- var status = ValueHelper.GetInstance(Endian).GetUShort(messageBytes, ref flag);
- var eventCount = ValueHelper.GetInstance(Endian).GetUShort(messageBytes, ref flag);
- var messageCount = ValueHelper.GetInstance(Endian).GetUShort(messageBytes, ref flag);
- var events = new byte[byteCount - 6];
- Array.Copy(messageBytes, 9, events, 0, byteCount - 6);
- return new GetCommEventLogModbusOutputStruct(slaveAddress, functionCode, byteCount, status, eventCount, messageCount, events);
- }
- }
-
- #endregion
-
- #region 报告从站ID
-
- ///
- /// 报告从站ID输入
- ///
- public class ReportSlaveIdModbusInputStruct : IInputStruct
- {
- ///
- /// 构造函数
- ///
- /// 从站号
- public ReportSlaveIdModbusInputStruct(byte slaveAddress)
- {
- SlaveAddress = slaveAddress;
- FunctionCode = (byte)ModbusSerialPortOnlyFunctionCode.ReportSlaveID;
- }
-
- ///
- /// 从站号
- ///
- public byte SlaveAddress { get; }
-
- ///
- /// 功能码
- ///
- public byte FunctionCode { get; }
- }
-
- ///
- /// 报告从站ID输出
- ///
- public class ReportSlaveIdModbusOutputStruct : IOutputStruct
- {
- ///
- /// 构造函数
- ///
- /// 从站号
- /// 功能码
- /// 字节个数
- /// 从站ID
- /// 运行状态
- /// 附加信息
- public ReportSlaveIdModbusOutputStruct(byte slaveAddress, byte functionCode,
- byte byteCount, byte slaveId, byte runIndicatorStatus, byte[] additionalData)
- {
- SlaveAddress = slaveAddress;
- FunctionCode = functionCode;
- ByteCount = byteCount;
- SlaveId = slaveId;
- RunIndicatorStatus = runIndicatorStatus;
- AdditionalData = additionalData;
- }
-
- ///
- /// 从站号
- ///
- public byte SlaveAddress { get; private set; }
-
- ///
- /// 功能码
- ///
- public byte FunctionCode { get; private set; }
-
- ///
- /// 字节个数
- ///
- public byte ByteCount { get; private set; }
-
- ///
- /// 从站ID
- ///
- public byte SlaveId { get; private set; }
-
- ///
- /// 运行状态
- ///
- public byte RunIndicatorStatus { get; private set; }
-
- ///
- /// 附加信息
- ///
- public byte[] AdditionalData { get; private set; }
- }
-
- ///
- /// 报告从站ID协议
- ///
- public class ReportSlaveIdModbusProtocol : ProtocolUnit
- {
- ///
- /// 格式化
- ///
- /// 写寄存器参数
- /// 写寄存器协议核心
- public override byte[] Format(IInputStruct message)
- {
- var r_message = (ReportSlaveIdModbusInputStruct)message;
-
- var formattingBytes = Format(r_message.SlaveAddress, r_message.FunctionCode);
-
- return formattingBytes;
- }
-
- ///
- /// 反格式化
- ///
- /// 设备返回的信息
- /// 当前反格式化的位置
- /// 反格式化的信息
- public override IOutputStruct Unformat(byte[] messageBytes, ref int flag)
- {
- var slaveAddress = ValueHelper.GetInstance(Endian).GetByte(messageBytes, ref flag);
- var functionCode = ValueHelper.GetInstance(Endian).GetByte(messageBytes, ref flag);
- var byteCount = ValueHelper.GetInstance(Endian).GetByte(messageBytes, ref flag);
- var slaveId = ValueHelper.GetInstance(Endian).GetByte(messageBytes, ref flag);
- var runIndicatorStatus = ValueHelper.GetInstance(Endian).GetByte(messageBytes, ref flag);
- var additionalData = new byte[byteCount - 2];
- Array.Copy(messageBytes, 5, additionalData, 0, byteCount - 2);
- return new ReportSlaveIdModbusOutputStruct(slaveAddress, functionCode, byteCount, slaveId, runIndicatorStatus, additionalData);
- }
- }
-
- #endregion
-
- #region 读文件记录
-
- ///
- /// 读文件输入数据定义
- ///
- public class ReadFileRecordInputDef
- {
- ///
- /// 引用类型,Modbus里固定为6
- ///
- public byte RefrenceType { get; } = 6;
- ///
- /// 文件计数
- ///
- public ushort FileNumber { get; set; }
- ///
- /// 记录计数
- ///
- public ushort RecordNumber { get; set; }
- ///
- /// 记录长度
- ///
- public ushort RecordLength { get; set; }
- }
-
- ///
- /// 读文件输出数据定义
- ///
- public class ReadFileRecordOutputDef
- {
- ///
- /// 返回长度
- ///
- public byte ResponseLength { get; set; }
- ///
- /// 引用类型
- ///
- public byte RefrenceType { get; set; }
- ///
- /// 记录数据
- ///
- public ushort[] RecordData { get; set; }
- }
-
- ///
- /// 读文件记录输入
- ///
- public class ReadFileRecordModbusInputStruct : IInputStruct
- {
- ///
- /// 构造函数
- ///
- /// 从站号
- /// 读文件记录定义
- public ReadFileRecordModbusInputStruct(byte slaveAddress, ReadFileRecordInputDef[] recordDefs)
- {
- SlaveAddress = slaveAddress;
- FunctionCode = (byte)ModbusProtocolFunctionCode.ReadFileRecord;
- ByteCount = (byte)(recordDefs.Count() * 7);
- RecordDefs = recordDefs.Clone() as ReadFileRecordInputDef[];
- }
-
- ///
- /// 从站号
- ///
- public byte SlaveAddress { get; }
-
- ///
- /// 功能码
- ///
- public byte FunctionCode { get; }
-
- ///
- /// 字节长度
- ///
- public byte ByteCount { get; }
-
- ///
- /// 记录定义
- ///
- public ReadFileRecordInputDef[] RecordDefs { get; }
- }
-
- ///
- /// 读文件记录输出
- ///
- public class ReadFileRecordModbusOutputStruct : IOutputStruct
- {
- ///
- /// 构造函数
- ///
- /// 从站号
- /// 功能码
- /// 字节个数
- /// 文件记录返回结果
- public ReadFileRecordModbusOutputStruct(byte slaveAddress, byte functionCode,
- byte byteCount, ReadFileRecordOutputDef[] recordDefs)
- {
- SlaveAddress = slaveAddress;
- FunctionCode = functionCode;
- ByteCount = byteCount;
- RecordDefs = recordDefs;
- }
-
- ///
- /// 从站号
- ///
- public byte SlaveAddress { get; private set; }
-
- ///
- /// 功能码
- ///
- public byte FunctionCode { get; private set; }
-
- ///
- /// 字节个数
- ///
- public byte ByteCount { get; private set; }
-
- ///
- /// 输出记录
- ///
- public ReadFileRecordOutputDef[] RecordDefs { get; private set; }
- }
-
- ///
- /// 读文件记录协议
- ///
- public class ReadFileRecordModbusProtocol : ProtocolUnit
- {
- ///
- /// 格式化
- ///
- /// 写寄存器参数
- /// 写寄存器协议核心
- public override byte[] Format(IInputStruct message)
- {
- var r_message = (ReadFileRecordModbusInputStruct)message;
-
- var formattingBytes = Format(r_message.SlaveAddress, r_message.FunctionCode);
-
- foreach (var def in r_message.RecordDefs)
- {
- formattingBytes = Format(formattingBytes, def.RefrenceType, def.FileNumber, def.RecordNumber, def.RecordLength);
- }
-
- return formattingBytes;
- }
-
- ///
- /// 反格式化
- ///
- /// 设备返回的信息
- /// 当前反格式化的位置
- /// 反格式化的信息
- public override IOutputStruct Unformat(byte[] messageBytes, ref int flag)
- {
- var slaveAddress = ValueHelper.GetInstance(Endian).GetByte(messageBytes, ref flag);
- var functionCode = ValueHelper.GetInstance(Endian).GetByte(messageBytes, ref flag);
- var byteCount = ValueHelper.GetInstance(Endian).GetByte(messageBytes, ref flag);
- List records = new List();
- for (int i = 0; i < byteCount; i++)
- {
- ReadFileRecordOutputDef record = new ReadFileRecordOutputDef();
- var fileResponseLength = ValueHelper.GetInstance(Endian).GetByte(messageBytes, ref flag);
- record.ResponseLength = fileResponseLength;
- i++;
- var fileReferenceType = ValueHelper.GetInstance(Endian).GetByte(messageBytes, ref flag);
- record.RefrenceType = fileReferenceType;
- i++;
- var recordLength = (fileResponseLength - 1) / 2;
- ushort[] recordContent = new ushort[recordLength];
- for (int j = 0; j < recordLength; j++)
- {
- recordContent[j] = ValueHelper.GetInstance(Endian).GetUShort(messageBytes, ref flag);
- i += 2;
- }
- record.RecordData = recordContent;
- records.Add(record);
- }
- return new ReadFileRecordModbusOutputStruct(slaveAddress, functionCode, byteCount, records.ToArray());
- }
- }
-
- #endregion
-
- #region 写文件记录
-
- ///
- /// 写文件记录输入定义
- ///
- public class WriteFileRecordInputDef
- {
- ///
- /// 引用类型,Modbus固定为6
- ///
- public byte RefrenceType { get; } = 6;
- ///
- /// 文件计数
- ///
- public ushort FileNumber { get; set; }
- ///
- /// 记录计数
- ///
- public ushort RecordNumber { get; set; }
- ///
- /// 记录长度
- ///
- public ushort RecordLength => RecordData != null ? (ushort)RecordData.Length : (ushort)0;
- ///
- /// 记录数据
- ///
- public ushort[] RecordData { get; set; }
- }
-
- ///
- /// 写文件记录输出定义
- ///
- public class WriteFileRecordOutputDef
- {
- ///
- /// 引用类型
- ///
- public byte RefrenceType { get; set; }
- ///
- /// 文件计数
- ///
- public ushort FileNumber { get; set; }
- ///
- /// 记录计数
- ///
- public ushort RecordNumber { get; set; }
- ///
- /// 记录长度
- ///
- public ushort RecordLength { get; set; }
- ///
- /// 记录数据
- ///
- public ushort[] RecordData { get; set; }
- }
-
- ///
- /// 写文件记录输入
- ///
- public class WriteFileRecordModbusInputStruct : IInputStruct
- {
- ///
- /// 构造函数
- ///
- /// 从站号
- /// 写文件记录内容
- public WriteFileRecordModbusInputStruct(byte slaveAddress, WriteFileRecordInputDef[] writeRecords)
- {
- SlaveAddress = slaveAddress;
- FunctionCode = (byte)ModbusProtocolFunctionCode.WriteFileRecord;
- byte count = 0;
- foreach (var writeRecord in writeRecords)
- {
- count += (byte)(writeRecord.RecordData.Length * 2 + 7);
- }
- ByteCount = count;
- WriteRecords = writeRecords.Clone() as WriteFileRecordInputDef[];
- }
-
- ///
- /// 从站号
- ///
- public byte SlaveAddress { get; }
-
- ///
- /// 功能码
- ///
- public byte FunctionCode { get; }
-
- ///
- /// 字节个数
- ///
- public byte ByteCount { get; private set; }
-
- ///
- /// 写记录数据
- ///
- public WriteFileRecordInputDef[] WriteRecords { get; private set; }
- }
-
- ///
- /// 写文件记录输出
- ///
- public class WriteFileRecordModbusOutputStruct : IOutputStruct
- {
- ///
- /// 构造函数
- ///
- /// 从站号
- /// 功能码
- /// 字节个数
- /// 写文件记录返回
- public WriteFileRecordModbusOutputStruct(byte slaveAddress, byte functionCode,
- byte byteCount, WriteFileRecordOutputDef[] writeRecords)
- {
- SlaveAddress = slaveAddress;
- FunctionCode = functionCode;
- ByteCount = byteCount;
- WriteRecords = writeRecords;
- }
-
- ///
- /// 从站号
- ///
- public byte SlaveAddress { get; private set; }
-
- ///
- /// 功能码
- ///
- public byte FunctionCode { get; private set; }
-
- ///
- /// 字节个数
- ///
- public byte ByteCount { get; private set; }
-
- ///
- /// 写记录数据
- ///
- public WriteFileRecordOutputDef[] WriteRecords { get; private set; }
- }
-
- ///
- /// 写文件记录协议
- ///
- public class WriteFileRecordModbusProtocol : ProtocolUnit
- {
- ///
- /// 格式化
- ///
- /// 写寄存器参数
- /// 写寄存器协议核心
- public override byte[] Format(IInputStruct message)
- {
- var r_message = (WriteFileRecordModbusInputStruct)message;
-
- var formattingBytes = Format(r_message.SlaveAddress, r_message.FunctionCode, r_message.ByteCount);
-
- foreach (var record in r_message.WriteRecords)
- {
- formattingBytes = Format(formattingBytes, record.RefrenceType, record.FileNumber, record.RecordNumber, record.RecordLength, record.RecordData);
- }
-
- return formattingBytes;
- }
-
- ///
- /// 反格式化
- ///
- /// 设备返回的信息
- /// 当前反格式化的位置
- /// 反格式化的信息
- public override IOutputStruct Unformat(byte[] messageBytes, ref int flag)
- {
- var slaveAddress = ValueHelper.GetInstance(Endian).GetByte(messageBytes, ref flag);
- var functionCode = ValueHelper.GetInstance(Endian).GetByte(messageBytes, ref flag);
- var byteCount = ValueHelper.GetInstance(Endian).GetByte(messageBytes, ref flag);
- List records = new List();
- for (int i = 0; i < byteCount; i++)
- {
- WriteFileRecordOutputDef record = new WriteFileRecordOutputDef();
- var fileReferenceType = ValueHelper.GetInstance(Endian).GetByte(messageBytes, ref flag);
- record.RefrenceType = fileReferenceType;
- i++;
- var fileNumber = ValueHelper.GetInstance(Endian).GetUShort(messageBytes, ref flag);
- record.FileNumber = fileNumber;
- i += 2;
- var fileRecordNumber = ValueHelper.GetInstance(Endian).GetUShort(messageBytes, ref flag);
- record.RecordNumber = fileRecordNumber;
- i += 2;
- var fileRecordLength = ValueHelper.GetInstance(Endian).GetUShort(messageBytes, ref flag);
- record.RecordLength = fileRecordLength;
- i += 2;
- ushort[] recordContent = new ushort[fileRecordLength];
- for (int j = 0; j < fileRecordLength; j++)
- {
- recordContent[j] = ValueHelper.GetInstance(Endian).GetUShort(messageBytes, ref flag);
- i += 2;
- }
- record.RecordData = recordContent;
- records.Add(record);
- }
- return new WriteFileRecordModbusOutputStruct(slaveAddress, functionCode, byteCount, records.ToArray());
- }
- }
-
- #endregion
-
- #region 写掩码
-
- ///
- /// 写文件记录输入
- ///
- public class MaskWriteRegisterModbusInputStruct : IInputStruct
- {
- ///
- /// 构造函数
- ///
- /// 从站号
- /// 地址索引
- /// 与掩码
- /// 或掩码
- public MaskWriteRegisterModbusInputStruct(byte slaveAddress, ushort referenceAddress, ushort andMask, ushort orMask)
- {
- SlaveAddress = slaveAddress;
- FunctionCode = (byte)ModbusProtocolFunctionCode.MaskWriteRegister;
- ReferenceAddress = referenceAddress;
- AndMask = andMask;
- OrMask = orMask;
- }
-
- ///
- /// 从站号
- ///
- public byte SlaveAddress { get; }
-
- ///
- /// 功能码
- ///
- public byte FunctionCode { get; }
-
- ///
- /// 地址索引
- ///
- public ushort ReferenceAddress { get; }
-
- ///
- /// 与掩码
- ///
- public ushort AndMask { get; }
-
- ///
- /// 或掩码
- ///
- public ushort OrMask { get; }
- }
-
- ///
- /// 写文件记录输出
- ///
- public class MaskWriteRegisterModbusOutputStruct : IOutputStruct
- {
- ///
- /// 构造函数
- ///
- /// 从站号
- /// 功能码
- /// 地址索引
- /// 与掩码
- /// 或掩码
- public MaskWriteRegisterModbusOutputStruct(byte slaveAddress, byte functionCode,
- ushort referenceAddress, ushort andMask, ushort orMask)
- {
- SlaveAddress = slaveAddress;
- FunctionCode = functionCode;
- ReferenceAddress = referenceAddress;
- AndMask = andMask;
- OrMask = orMask;
- }
-
- ///
- /// 从站号
- ///
- public byte SlaveAddress { get; private set; }
-
- ///
- /// 功能码
- ///
- public byte FunctionCode { get; private set; }
-
- ///
- /// 地址索引
- ///
- public ushort ReferenceAddress { get; }
-
- ///
- /// 与掩码
- ///
- public ushort AndMask { get; }
-
- ///
- /// 或掩码
- ///
- public ushort OrMask { get; }
- }
-
- ///
- /// 写文件记录协议
- ///
- public class MaskWriteRegisterModbusProtocol : ProtocolUnit
- {
- ///
- /// 格式化
- ///
- /// 写寄存器参数
- /// 写寄存器协议核心
- public override byte[] Format(IInputStruct message)
- {
- var r_message = (MaskWriteRegisterModbusInputStruct)message;
-
- var formattingBytes = Format(r_message.SlaveAddress, r_message.FunctionCode, r_message.ReferenceAddress, r_message.AndMask, r_message.OrMask);
-
- return formattingBytes;
- }
-
- ///
- /// 反格式化
- ///
- /// 设备返回的信息
- /// 当前反格式化的位置
- /// 反格式化的信息
- public override IOutputStruct Unformat(byte[] messageBytes, ref int flag)
- {
- var slaveAddress = ValueHelper.GetInstance(Endian).GetByte(messageBytes, ref flag);
- var functionCode = ValueHelper.GetInstance(Endian).GetByte(messageBytes, ref flag);
- var referenceAddress = ValueHelper.GetInstance(Endian).GetUShort(messageBytes, ref flag);
- var andMask = ValueHelper.GetInstance(Endian).GetUShort(messageBytes, ref flag);
- var orMask = ValueHelper.GetInstance(Endian).GetUShort(messageBytes, ref flag);
- return new MaskWriteRegisterModbusOutputStruct(slaveAddress, functionCode, referenceAddress, andMask, orMask);
- }
- }
-
- #endregion
-
- #region 读写多个输入寄存器
-
- ///
- /// 读写多个输入寄存器输入
- ///
- public class ReadWriteMultipleRegistersModbusInputStruct : IInputStruct
- {
- ///
- /// 构造函数
- ///
- /// 从站号
- /// 读起始地址
- /// 读个数
- /// 写起始地址
- /// 写内容
- public ReadWriteMultipleRegistersModbusInputStruct(byte slaveAddress, ushort readStartingAddress, ushort quantityToRead, ushort writeStartingAddress, ushort[] writeValues)
- {
- SlaveAddress = slaveAddress;
- FunctionCode = (byte)ModbusProtocolFunctionCode.ReadWriteMultipleRegister;
- ReadStartingAddress = readStartingAddress;
- QuantityToRead = quantityToRead;
- WriteStartingAddress = writeStartingAddress;
- QuantityToWrite = (ushort)writeValues.Count();
- WriteByteCount = (ushort)(QuantityToWrite * 2);
- WriteValues = writeValues.Clone() as ushort[];
- }
-
- ///
- /// 从站号
- ///
- public byte SlaveAddress { get; }
-
- ///
- /// 功能码
- ///
- public byte FunctionCode { get; }
-
- ///
- /// 读起始地址
- ///
- public ushort ReadStartingAddress { get; }
-
- ///
- /// 读个数
- ///
- public ushort QuantityToRead { get; }
-
- ///
- /// 写起始地址
- ///
- public ushort WriteStartingAddress { get; }
-
- ///
- /// 写个数
- ///
- public ushort QuantityToWrite { get; }
-
- ///
- /// 写字节个数
- ///
- public ushort WriteByteCount { get; }
-
- ///
- /// 写数据
- ///
- public ushort[] WriteValues { get; }
- }
-
- ///
- /// 读写多个输入寄存器输出
- ///
- public class ReadWriteMultipleRegistersModbusOutputStruct : IOutputStruct
- {
- ///
- /// 构造函数
- ///
- /// 从站号
- /// 功能码
- /// 获取的字节数
- /// 读取的寄存器内容
- public ReadWriteMultipleRegistersModbusOutputStruct(byte slaveAddress, byte functionCode, byte byteCount, ushort[] readRegisterValues)
- {
- SlaveAddress = slaveAddress;
- FunctionCode = functionCode;
- ByteCount = byteCount;
- ReadRegisterValues = readRegisterValues;
- }
-
- ///
- /// 从站号
- ///
- public byte SlaveAddress { get; private set; }
-
- ///
- /// 功能码
- ///
- public byte FunctionCode { get; private set; }
-
- ///
- /// 字节个数
- ///
- public byte ByteCount { get; private set; }
-
- ///
- /// 读数据内容
- ///
- public ushort[] ReadRegisterValues { get; private set; }
- }
-
- ///
- /// 读写多个输入寄存器协议
- ///
- public class ReadWriteMultipleRegistersModbusProtocol : ProtocolUnit
- {
- ///
- /// 格式化
- ///
- /// 写寄存器参数
- /// 写寄存器协议核心
- public override byte[] Format(IInputStruct message)
- {
- var r_message = (ReadWriteMultipleRegistersModbusInputStruct)message;
-
- var formattingBytes = Format(r_message.SlaveAddress, r_message.FunctionCode, r_message.ReadStartingAddress,
- r_message.QuantityToRead, r_message.WriteStartingAddress, r_message.QuantityToWrite,
- r_message.WriteByteCount, r_message.WriteValues);
-
- return formattingBytes;
- }
-
- ///
- /// 反格式化
- ///
- /// 设备返回的信息
- /// 当前反格式化的位置
- /// 反格式化的信息
- public override IOutputStruct Unformat(byte[] messageBytes, ref int flag)
- {
- var slaveAddress = ValueHelper.GetInstance(Endian).GetByte(messageBytes, ref flag);
- var functionCode = ValueHelper.GetInstance(Endian).GetByte(messageBytes, ref flag);
- var byteCount = ValueHelper.GetInstance(Endian).GetByte(messageBytes, ref flag);
- ushort[] readValues = new ushort[byteCount / 2];
- for (int i = 0; i < byteCount / 2; i++)
- {
- readValues[i] = ValueHelper.GetInstance(Endian).GetUShort(messageBytes, ref flag);
- }
- return new ReadWriteMultipleRegistersModbusOutputStruct(slaveAddress, functionCode, byteCount, readValues);
- }
- }
-
- #endregion
-
- #region 读FIFO队列
-
- ///
- /// 读写多个输入寄存器输入
- ///
- public class ReadFIFOQueueModbusInputStruct : IInputStruct
- {
- ///
- /// 构造函数
- ///
- /// 从站号
- /// FIFO队列地址索引
- public ReadFIFOQueueModbusInputStruct(byte slaveAddress, ushort fifoPointerAddress)
- {
- SlaveAddress = slaveAddress;
- FunctionCode = (byte)ModbusProtocolFunctionCode.ReadFIFOQueue;
- FIFOPointerAddress = fifoPointerAddress;
- }
-
- ///
- /// 从站号
- ///
- public byte SlaveAddress { get; }
-
- ///
- /// 功能码
- ///
- public byte FunctionCode { get; }
-
- ///
- /// FIFO地址
- ///
- public ushort FIFOPointerAddress { get; }
- }
-
- ///
- /// 读写多个输入寄存器输出
- ///
- public class ReadFIFOQueueModbusOutputStruct : IOutputStruct
- {
- ///
- /// 构造函数
- ///
- /// 从站号
- /// 功能码
- /// 获取的字节数
- /// FIFO个数
- /// FIFO值
- public ReadFIFOQueueModbusOutputStruct(byte slaveAddress, byte functionCode, ushort byteCount, ushort fifoCount, ushort[] fifoValueRegister)
- {
- SlaveAddress = slaveAddress;
- FunctionCode = functionCode;
- ByteCount = byteCount;
- FIFOCount = fifoCount;
- FIFOValueRegister = fifoValueRegister;
- }
-
- ///
- /// 从站号
- ///
- public byte SlaveAddress { get; private set; }
-
- ///
- /// 功能码
- ///
- public byte FunctionCode { get; private set; }
-
- ///
- /// 字节个数
- ///
- public ushort ByteCount { get; private set; }
-
- ///
- /// FIFO个数
- ///
- public ushort FIFOCount { get; private set; }
-
- ///
- /// FIFO内容
- ///
- public ushort[] FIFOValueRegister { get; private set; }
- }
-
- ///
- /// 读写多个输入寄存器协议
- ///
- public class ReadFIFOQueueModbusProtocol : ProtocolUnit
- {
- ///
- /// 格式化
- ///
- /// 写寄存器参数
- /// 写寄存器协议核心
- public override byte[] Format(IInputStruct message)
- {
- var r_message = (ReadFIFOQueueModbusInputStruct)message;
-
- var formattingBytes = Format(r_message.SlaveAddress, r_message.FunctionCode, r_message.FIFOPointerAddress);
-
- return formattingBytes;
- }
-
- ///
- /// 反格式化
- ///
- /// 设备返回的信息
- /// 当前反格式化的位置
- /// 反格式化的信息
- public override IOutputStruct Unformat(byte[] messageBytes, ref int flag)
- {
- var slaveAddress = ValueHelper.GetInstance(Endian).GetByte(messageBytes, ref flag);
- var functionCode = ValueHelper.GetInstance(Endian).GetByte(messageBytes, ref flag);
- var byteCount = ValueHelper.GetInstance(Endian).GetUShort(messageBytes, ref flag);
- var fifoCount = ValueHelper.GetInstance(Endian).GetUShort(messageBytes, ref flag);
- ushort[] readValues = new ushort[fifoCount];
- for (int i = 0; i < fifoCount; i++)
- {
- readValues[i] = ValueHelper.GetInstance(Endian).GetUShort(messageBytes, ref flag);
- }
- return new ReadFIFOQueueModbusOutputStruct(slaveAddress, functionCode, byteCount, fifoCount, readValues);
- }
- }
-
- #endregion
-
- ///
- /// Modbus协议错误表
- ///
- public class ModbusProtocolErrorException : ProtocolErrorException
- {
- private static readonly Dictionary ProtocolErrorDictionary = new Dictionary
- {
- {1, "ILLEGAL_FUNCTION"},
- {2, "ILLEGAL_DATA_ACCESS"},
- {3, "ILLEGAL_DATA_VALUE"},
- {4, "SLAVE_DEVICE_FAILURE"},
- {5, "ACKNOWLWDGE"},
- {6, "SLAVE_DEVICE_BUSY"},
- {8, "MEMORY_PARITY_ERROR" },
- {10, "GATEWAY_PATH_UNAVAILABLE"},
- {11, "GATEWAY_TARGET_DEVICE_FAILED_TO_RESPOND"}
- };
-
- ///
- /// Modbus错误
- ///
- /// Modbus错误号
- public ModbusProtocolErrorException(int messageNumber)
- : base(ProtocolErrorDictionary[messageNumber])
- {
- ErrorMessageNumber = messageNumber;
- }
-
- ///
- /// Modbus错误号
- ///
- public int ErrorMessageNumber { get; private set; }
- }
-}
\ No newline at end of file
+}
diff --git a/Modbus.Net/Modbus.Net.Modbus/ModbusProtocolLinkerBytesExtend.cs b/Modbus.Net/Modbus.Net.Modbus/ModbusProtocolLinkerBytesExtend.cs
index 3b7f7cc..a494f6e 100644
--- a/Modbus.Net/Modbus.Net.Modbus/ModbusProtocolLinkerBytesExtend.cs
+++ b/Modbus.Net/Modbus.Net.Modbus/ModbusProtocolLinkerBytesExtend.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Collections.Generic;
using System.Globalization;
using System.Text;
@@ -6,47 +6,168 @@ using System.Text;
namespace Modbus.Net.Modbus
{
///
- /// Udp字节伸缩
+ /// Modbus 协议字节伸缩类 / Modbus Protocol Bytes Extend Classes
+ ///
+ /// 实现各种 Modbus 协议的字节扩展和收缩功能
+ /// Implements bytes extend and reduce functionality for various Modbus protocols
+ ///
+ /// 主要类 / Main Classes:
+ ///
+ /// - ModbusTcpProtocolLinkerBytesExtend - TCP 协议 MBAP 头处理 / TCP protocol MBAP header handling
+ /// - ModbusRtuProtocolLinkerBytesExtend - RTU 协议 CRC16 处理 / RTU protocol CRC16 handling
+ /// - ModbusAsciiProtocolLinkerBytesExtend - ASCII 协议 LRC 和格式处理 / ASCII protocol LRC and format handling
+ /// - ModbusRtuInTcpProtocolLinkerBytesExtend - RTU over TCP 透传 / RTU over TCP tunneling
+ /// - ModbusAsciiInTcpProtocolLinkerBytesExtend - ASCII over TCP 透传 / ASCII over TCP tunneling
+ /// - ModbusUdpProtocolLinkerBytesExtend - UDP 协议处理 / UDP protocol handling
+ ///
+ ///
+ ///
+ ///
+
+ #region UDP 协议字节伸缩 / UDP Protocol Bytes Extend
+
+ ///
+ /// Modbus RTU over UDP 字节伸缩 / Modbus RTU over UDP Bytes Extend
+ ///
+ /// 继承自 ModbusRtuProtocolLinkerBytesExtend,处理 RTU over UDP 协议
+ /// Inherits from ModbusRtuProtocolLinkerBytesExtend, handles RTU over UDP protocol
+ ///
+ /// 特点 / Characteristics:
+ ///
+ /// - RTU 帧原样传输 / RTU frames transmitted as-is
+ /// - 无 MBAP 头 / No MBAP header
+ /// - 保留 CRC16 校验 / CRC16 checksum preserved
+ /// - 适用于 UDP 广播 / Suitable for UDP broadcast
+ ///
+ ///
+ ///
///
public class ModbusRtuInUdpProtocolLinkerBytesExtend : ModbusRtuProtocolLinkerBytesExtend
{
-
+ // 使用 RTU 协议的字节伸缩方法 / Use RTU protocol bytes extend methods
}
///
- /// Udp字节伸缩
+ /// Modbus ASCII over UDP 字节伸缩 / Modbus ASCII over UDP Bytes Extend
+ ///
+ /// 继承自 ModbusAsciiProtocolLinkerBytesExtend,处理 ASCII over UDP 协议
+ /// Inherits from ModbusAsciiProtocolLinkerBytesExtend, handles ASCII over UDP protocol
+ ///
///
public class ModbusAsciiInUdpProtocolLinkerBytesExtend : ModbusAsciiProtocolLinkerBytesExtend
{
-
+ // 使用 ASCII 协议的字节伸缩方法 / Use ASCII protocol bytes extend methods
}
///
- /// Udp字节伸缩
+ /// Modbus UDP 协议字节伸缩 / Modbus UDP Protocol Bytes Extend
+ ///
+ /// 继承自 ModbusTcpProtocolLinkerBytesExtend,处理 UDP 协议
+ /// Inherits from ModbusTcpProtocolLinkerBytesExtend, handles UDP protocol
+ ///
+ /// 特点 / Characteristics:
+ ///
+ /// - 添加 MBAP 头 / Adds MBAP header
+ /// - 无连接模式 / Connectionless mode
+ /// - 适用于广播查询 / Suitable for broadcast query
+ ///
+ ///
+ ///
///
public class ModbusUdpProtocolLinkerBytesExtend : ModbusTcpProtocolLinkerBytesExtend
{
-
+ // 使用 TCP 协议的字节伸缩方法 / Use TCP protocol bytes extend methods
}
+ #endregion
+
+ #region TCP 透传协议字节伸缩 / TCP Tunneling Protocol Bytes Extend
+
///
- /// Rtu透传字节伸缩
+ /// Modbus RTU over TCP 字节伸缩 / Modbus RTU over TCP Bytes Extend
+ ///
+ /// 继承自 ModbusRtuProtocolLinkerBytesExtend,处理 RTU over TCP 透传协议
+ /// Inherits from ModbusRtuProtocolLinkerBytesExtend, handles RTU over TCP tunneling protocol
+ ///
+ /// 使用场景 / Use Cases:
+ ///
+ /// - 串口服务器 / Serial device server
+ /// - 通过 TCP 传输 RTU 帧 / Transmit RTU frames over TCP
+ /// - 远程串口访问 / Remote serial access
+ ///
+ ///
+ ///
+ /// 特点 / Characteristics:
+ ///
+ /// - RTU 帧原样传输 / RTU frames transmitted as-is
+ /// - 无 MBAP 头 / No MBAP header
+ /// - 保留 CRC16 校验 / CRC16 checksum preserved
+ ///
+ ///
+ ///
///
public class ModbusRtuInTcpProtocolLinkerBytesExtend : ModbusRtuProtocolLinkerBytesExtend
{
-
+ // 使用 RTU 协议的字节伸缩方法 / Use RTU protocol bytes extend methods
}
///
- /// Ascii透传字节伸缩
+ /// Modbus ASCII over TCP 字节伸缩 / Modbus ASCII over TCP Bytes Extend
+ ///
+ /// 继承自 ModbusAsciiProtocolLinkerBytesExtend,处理 ASCII over TCP 透传协议
+ /// Inherits from ModbusAsciiProtocolLinkerBytesExtend, handles ASCII over TCP tunneling protocol
+ ///
+ /// 使用场景 / Use Cases:
+ ///
+ /// - 串口服务器 / Serial device server
+ /// - 通过 TCP 传输 ASCII 帧 / Transmit ASCII frames over TCP
+ ///
+ ///
+ ///
///
public class ModbusAsciiInTcpProtocolLinkerBytesExtend : ModbusAsciiProtocolLinkerBytesExtend
{
-
+ // 使用 ASCII 协议的字节伸缩方法 / Use ASCII protocol bytes extend methods
}
+ #endregion
+
+ #region TCP 协议字节伸缩 / TCP Protocol Bytes Extend
+
///
- /// Tcp协议字节伸缩
+ /// Modbus/TCP 协议字节伸缩 / Modbus/TCP Protocol Bytes Extend
+ ///
+ /// 实现 Modbus TCP 协议的 MBAP 头添加和移除功能
+ /// Implements MBAP header addition and removal for Modbus TCP protocol
+ ///
+ /// MBAP 头格式 / MBAP Header Format:
+ ///
+ /// [Transaction ID (2)][Protocol ID (2)][Length (2)][Unit ID (1)]
+ /// │ │ │ │ │ │
+ /// └─ 事务标识,用于匹配请求响应
+ /// └─ 协议标识,Modbus=0
+ /// └─ 后续字节长度
+ /// └─ 从站地址
+ ///
+ ///
+ ///
+ /// 使用示例 / Usage Example:
+ ///
+ /// var bytesExtend = new ModbusTcpProtocolLinkerBytesExtend();
+ ///
+ /// // 扩展 (发送前) / Extend (before sending)
+ /// byte[] rawData = [0x01, 0x03, 0x00, 0x00, 0x00, 0x0A]; // Modbus RTU 帧
+ /// byte[] extendedData = bytesExtend.BytesExtend(rawData);
+ /// // 结果:[0x00 0x01 0x00 0x00 0x00 0x06 0x01 0x03 0x00 0x00 0x00 0x0A]
+ /// // │ 事务 ID │ 协议 ID │ 长度 │RTU 帧...
+ /// ///
+ /// // 收缩 (接收后) / Reduce (after receiving)
+ /// byte[] receivedData = [0x00 0x01 0x00 0x00 0x00 0x06 0x01 0x03 0x04 0x00 0x64 0x00 0xC8];
+ /// byte[] reducedData = bytesExtend.BytesDecact(receivedData);
+ /// // 结果:[0x01, 0x03, 0x04, 0x00, 0x64, 0x00, 0xC8] // 移除 MBAP 头
+ ///
+ ///
+ ///
///
public class ModbusTcpProtocolLinkerBytesExtend : IProtocolLinkerBytesExtend
{
@@ -54,124 +175,418 @@ namespace Modbus.Net.Modbus
private static readonly object _counterLock = new object();
///
- /// 协议扩展,协议内容发送前调用
+ /// 协议扩展 (发送前调用) / Protocol Extend (Called Before Sending)
+ ///
+ /// 在 Modbus TCP 协议数据前添加 MBAP 头 (6 字节)
+ /// Add MBAP header (6 bytes) before Modbus TCP protocol data
+ ///
+ /// MBAP 头结构 / MBAP Header Structure:
+ ///
+ /// - Transaction ID (2 字节) - 事务标识,用于匹配请求响应 / Transaction identifier for matching request-response
+ /// - Protocol ID (2 字节) - 协议标识,Modbus=0 / Protocol identifier, Modbus=0
+ /// - Length (2 字节) - 后续字节长度 / Length of following bytes
+ /// - Unit ID (1 字节) - 从站地址 / Unit identifier (slave address)
+ ///
+ ///
+ ///
+ /// 事务计数 / Transaction Counting:
+ ///
+ /// - 使用静态计数器 / Uses static counter
+ /// - 每次发送递增 / Increments on each send
+ /// - 模 65536 循环 (ushort 范围) / Cycles mod 65536 (ushort range)
+ /// - 线程安全 (使用锁) / Thread-safe (uses lock)
+ ///
+ ///
+ ///
///
- /// 扩展前的原始协议内容
- /// 扩展后的协议内容
+ ///
+ /// 扩展前的原始协议内容 / Original Protocol Content Before Extension
+ ///
+ /// Modbus RTU 帧 (包含从站地址、功能码、数据、CRC)
+ /// Modbus RTU frame (contains slave address, function code, data, CRC)
+ ///
+ ///
+ ///
+ /// 扩展后的协议内容 / Extended Protocol Content
+ ///
+ /// 添加了 MBAP 头的完整 TCP 帧
+ /// Complete TCP frame with MBAP header added
+ ///
+ ///
public byte[] BytesExtend(byte[] content)
{
- //Modbus/Tcp协议扩张,前面加6个字节,前面2个为事务编号,中间两个为0,后面两个为协议整体内容的长度
+ // Modbus/TCP 协议扩展,前面加 6 个字节
+ // Modbus/TCP protocol extension, add 6 bytes at the front
+ // 前面 2 个为事务编号,中间两个为 0,后面两个为协议整体内容的长度
+ // First 2: transaction ID, middle 2: 0, last 2: total length
var newFormat = new byte[6 + content.Length];
+
lock (_counterLock)
{
+ // 生成事务 ID (循环计数) / Generate transaction ID (cyclic count)
var transaction = (ushort)(_sendCount % 65536 + 1);
- var tag = (ushort)0;
- var leng = (ushort)content.Length;
+ var tag = (ushort)0; // 协议标识,Modbus=0 / Protocol ID, Modbus=0
+ var leng = (ushort)content.Length; // 后续字节长度 / Length of following bytes
+
+ // 复制事务 ID (大端格式) / Copy transaction ID (big-endian)
Array.Copy(BigEndianLsbValueHelper.Instance.GetBytes(transaction), 0, newFormat, 0, 2);
+ // 复制协议标识 / Copy protocol ID
Array.Copy(BigEndianLsbValueHelper.Instance.GetBytes(tag), 0, newFormat, 2, 2);
+ // 复制长度 / Copy length
Array.Copy(BigEndianLsbValueHelper.Instance.GetBytes(leng), 0, newFormat, 4, 2);
+ // 复制原始内容 / Copy original content
Array.Copy(content, 0, newFormat, 6, content.Length);
- _sendCount++;
+
+ _sendCount++; // 递增计数器 / Increment counter
}
return newFormat;
}
///
- /// 协议收缩,协议内容接收后调用
+ /// 协议收缩 (接收后调用) / Protocol Reduce (Called After Receiving)
+ ///
+ /// 移除 Modbus TCP 协议数据的 MBAP 头 (6 字节)
+ /// Remove MBAP header (6 bytes) from Modbus TCP protocol data
+ ///
+ /// 处理流程 / Processing Flow:
+ ///
+ /// - 创建新数组,长度 = 原长度 - 6 / Create new array, length = original length - 6
+ /// - 从第 7 个字节开始复制 / Copy starting from 7th byte
+ /// - 返回纯 Modbus RTU 帧 / Return pure Modbus RTU frame
+ ///
+ ///
+ ///
///
- /// 收缩前的完整协议内容
- /// 收缩后的协议内容
+ ///
+ /// 收缩前的完整协议内容 / Complete Protocol Content Before Reduction
+ ///
+ /// 包含 MBAP 头的完整 TCP 帧
+ /// Complete TCP frame with MBAP header
+ ///
+ ///
+ ///
+ /// 收缩后的协议内容 / Reduced Protocol Content
+ ///
+ /// 移除 MBAP 头后的 Modbus RTU 帧
+ /// Modbus RTU frame with MBAP header removed
+ ///
+ ///
public byte[] BytesDecact(byte[] content)
{
- //Modbus/Tcp协议收缩,抛弃前面6个字节的内容
+ // Modbus/TCP 协议收缩,抛弃前面 6 个字节的内容
+ // Modbus/TCP protocol reduction, discard first 6 bytes
var newContent = new byte[content.Length - 6];
Array.Copy(content, 6, newContent, 0, newContent.Length);
return newContent;
}
}
+ #endregion
+
+ #region RTU 协议字节伸缩 / RTU Protocol Bytes Extend
+
///
- /// Rtu协议字节伸缩
+ /// Modbus/RTU 协议字节伸缩 / Modbus/RTU Protocol Bytes Extend
+ ///
+ /// 实现 Modbus RTU 协议的 CRC16 校验码添加和移除功能
+ /// Implements CRC16 checksum addition and removal for Modbus RTU protocol
+ ///
+ /// CRC16 校验 / CRC16 Checksum:
+ ///
+ /// - 多项式:0xA001 / Polynomial: 0xA001
+ /// - 初始值:0xFFFF / Initial value: 0xFFFF
+ /// - 2 字节,低字节在前 / 2 bytes, low byte first
+ /// - 覆盖从站地址到数据的所有字节 / Covers all bytes from slave address to data
+ ///
+ ///
+ ///
+ /// 使用示例 / Usage Example:
+ ///
+ /// var bytesExtend = new ModbusRtuProtocolLinkerBytesExtend();
+ ///
+ /// // 扩展 (发送前) / Extend (before sending)
+ /// byte[] rawData = [0x01, 0x03, 0x00, 0x00, 0x00, 0x0A]; // Modbus 请求
+ /// byte[] extendedData = bytesExtend.BytesExtend(rawData);
+ /// // 结果:[0x01, 0x03, 0x00, 0x00, 0x00, 0x0A, 0xC4, 0x0B]
+ /// // │ CRC16 │
+ /// ///
+ /// // 收缩 (接收后) / Reduce (after receiving)
+ /// byte[] receivedData = [0x01, 0x03, 0x04, 0x00, 0x64, 0x00, 0xC8, 0xB9, 0x0A];
+ /// byte[] reducedData = bytesExtend.BytesDecact(receivedData);
+ /// // 结果:[0x01, 0x03, 0x04, 0x00, 0x64, 0x00, 0xC8] // 移除 CRC
+ ///
+ ///
+ ///
///
public class ModbusRtuProtocolLinkerBytesExtend : IProtocolLinkerBytesExtend
{
///
- /// 协议扩展,协议内容发送前调用
+ /// 协议扩展 (发送前调用) / Protocol Extend (Called Before Sending)
+ ///
+ /// 在 Modbus RTU 协议数据后添加 CRC16 校验码 (2 字节)
+ /// Add CRC16 checksum (2 bytes) after Modbus RTU protocol data
+ ///
+ /// 处理流程 / Processing Flow:
+ ///
+ /// - 创建新数组,长度 = 原长度 + 2 / Create new array, length = original length + 2
+ /// - 计算原始数据的 CRC16 / Calculate CRC16 of original data
+ /// - 复制原始数据到新数组 / Copy original data to new array
+ /// - 在末尾添加 CRC16 (低字节在前) / Add CRC16 at end (low byte first)
+ ///
+ ///
+ ///
///
- /// 扩展前的原始协议内容
- /// 扩展后的协议内容
+ ///
+ /// 扩展前的原始协议内容 / Original Protocol Content Before Extension
+ ///
+ /// 不包含 CRC 的 Modbus RTU 帧
+ /// Modbus RTU frame without CRC
+ ///
+ ///
+ ///
+ /// 扩展后的协议内容 / Extended Protocol Content
+ ///
+ /// 添加了 CRC16 校验码的完整 RTU 帧
+ /// Complete RTU frame with CRC16 checksum added
+ ///
+ ///
public byte[] BytesExtend(byte[] content)
{
var crc = new byte[2];
- //Modbus/Rtu协议扩张,增加CRC校验
+ // Modbus/RTU 协议扩展,增加 CRC 校验
+ // Modbus/RTU protocol extension, add CRC checksum
var newFormat = new byte[content.Length + 2];
+
+ // 计算 CRC16 / Calculate CRC16
Crc16.GetInstance().GetCRC(content, ref crc);
+
+ // 复制原始内容 / Copy original content
Array.Copy(content, 0, newFormat, 0, content.Length);
+ // 在末尾添加 CRC (低字节在前) / Add CRC at end (low byte first)
Array.Copy(crc, 0, newFormat, newFormat.Length - 2, crc.Length);
+
return newFormat;
}
///
- /// 协议收缩,协议内容接收后调用
+ /// 协议收缩 (接收后调用) / Protocol Reduce (Called After Receiving)
+ ///
+ /// 移除 Modbus RTU 协议数据的 CRC16 校验码 (2 字节)
+ /// Remove CRC16 checksum (2 bytes) from Modbus RTU protocol data
+ ///
+ /// 处理流程 / Processing Flow:
+ ///
+ /// - 创建新数组,长度 = 原长度 - 2 / Create new array, length = original length - 2
+ /// - 复制除最后 2 字节外的所有数据 / Copy all data except last 2 bytes
+ /// - 返回不含 CRC 的数据 / Return data without CRC
+ ///
+ ///
+ ///
+ /// 注意 / Note:
+ ///
+ /// - CRC 校验在收缩前已完成 / CRC check is done before reduction
+ /// - 此方法仅移除 CRC 字节 / This method only removes CRC bytes
+ ///
+ ///
+ ///
///
- /// 收缩前的完整协议内容
- /// 收缩后的协议内容
+ ///
+ /// 收缩前的完整协议内容 / Complete Protocol Content Before Reduction
+ ///
+ /// 包含 CRC16 校验码的完整 RTU 帧
+ /// Complete RTU frame with CRC16 checksum
+ ///
+ ///
+ ///
+ /// 收缩后的协议内容 / Reduced Protocol Content
+ ///
+ /// 移除 CRC16 校验码后的 Modbus RTU 帧
+ /// Modbus RTU frame with CRC16 checksum removed
+ ///
+ ///
public byte[] BytesDecact(byte[] content)
{
- //Modbus/Rtu协议收缩,抛弃后面2个字节的内容
+ // Modbus/RTU 协议收缩,抛弃最后 2 个字节的内容 (CRC)
+ // Modbus/RTU protocol reduction, discard last 2 bytes (CRC)
var newContent = new byte[content.Length - 2];
Array.Copy(content, 0, newContent, 0, newContent.Length);
return newContent;
}
}
+ #endregion
+
+ #region ASCII 协议字节伸缩 / ASCII Protocol Bytes Extend
+
///
- /// Ascii协议字节伸缩
+ /// Modbus/ASCII 协议字节伸缩 / Modbus/ASCII Protocol Bytes Extend
+ ///
+ /// 实现 Modbus ASCII 协议的格式转换和 LRC 校验功能
+ /// Implements format conversion and LRC checksum for Modbus ASCII protocol
+ ///
+ /// ASCII 协议格式 / ASCII Protocol Format:
+ ///
+ /// : [从站地址][功能码][数据][LRC][CR][LF]
+ /// │ │ │ │ │ │
+ /// │ │ │ │ │ └─ 换行符 (0x0A)
+ /// │ │ │ │ └─ 回车符 (0x0D)
+ /// │ │ │ └─ LRC 校验 (2 字符十六进制)
+ /// │ │ └─ 数据 (十六进制 ASCII 字符)
+ /// │ └─ 功能码 (2 字符十六进制)
+ /// └─ 起始符 (冒号 0x3A)
+ ///
+ ///
+ ///
+ /// 编码规则 / Encoding Rules:
+ ///
+ /// - 每个字节转换为 2 个十六进制 ASCII 字符 / Each byte converted to 2 hex ASCII characters
+ /// - 例如:0x1A → "1A" (0x31, 0x41) / e.g., 0x1A → "1A" (0x31, 0x41)
+ /// - LRC 校验:纵向冗余校验 / LRC checksum: Longitudinal Redundancy Check
+ ///
+ ///
+ ///
///
public class ModbusAsciiProtocolLinkerBytesExtend : IProtocolLinkerBytesExtend
{
///
- /// 协议扩展,协议内容发送前调用
+ /// 协议扩展 (发送前调用) / Protocol Extend (Called Before Sending)
+ ///
+ /// 将二进制数据转换为 ASCII 格式,添加起始符、LRC 校验和结束符
+ /// Convert binary data to ASCII format, add start marker, LRC checksum and end marker
+ ///
+ /// 处理流程 / Processing Flow:
+ ///
+ /// - 添加起始符 ':' (0x3A) / Add start marker ':' (0x3A)
+ /// - 将每个字节转换为 2 个十六进制 ASCII 字符 / Convert each byte to 2 hex ASCII characters
+ /// - 计算 LRC 校验码并转换为 ASCII / Calculate LRC checksum and convert to ASCII
+ /// - 添加结束符 CR LF (0x0D 0x0A) / Add end marker CR LF (0x0D 0x0A)
+ ///
+ ///
+ ///
+ /// 示例 / Example:
+ ///
+ /// 原始数据 / Raw data: [0x01, 0x03, 0x00, 0x00]
+ /// 扩展后 / Extended: [0x3A, 0x30, 0x31, 0x30, 0x33, 0x30, 0x30, 0x30, 0x30, LRC, 0x0D, 0x0A]
+ /// ASCII: ":01030000[LRC][CR][LF]"
+ ///
+ ///
+ ///
///
- /// 扩展前的原始协议内容
- /// 扩展后的协议内容
+ ///
+ /// 扩展前的原始协议内容 / Original Protocol Content Before Extension
+ ///
+ /// 二进制格式的 Modbus 数据
+ /// Binary format Modbus data
+ ///
+ ///
+ ///
+ /// 扩展后的协议内容 / Extended Protocol Content
+ ///
+ /// ASCII 格式的完整帧
+ /// Complete frame in ASCII format
+ ///
+ ///
public byte[] BytesExtend(byte[] content)
{
- //Modbus/Ascii协议扩张,前面增加:,后面增加LRC校验和尾字符
+ // Modbus/ASCII 协议扩张,前面增加:,后面增加 LRC 校验和尾字符
+ // Modbus/ASCII protocol extension, add ':' at front, LRC and tail characters at end
var newContent = new List();
+
+ // 添加起始符 ':' / Add start marker ':'
newContent.AddRange(Encoding.ASCII.GetBytes(":"));
+
+ // 将每个字节转换为 2 个十六进制 ASCII 字符 / Convert each byte to 2 hex ASCII characters
foreach (var number in content)
newContent.AddRange(Encoding.ASCII.GetBytes(number.ToString("X2")));
+
+ // 计算并添加 LRC 校验 / Calculate and add LRC checksum
newContent.AddRange(Encoding.ASCII.GetBytes(Crc16.GetInstance().GetLRC(content)));
+
+ // 添加结束符 CR LF / Add end marker CR LF
newContent.Add(0x0d);
newContent.Add(0x0a);
+
return newContent.ToArray();
}
///
- /// 协议收缩,协议内容接收后调用
+ /// 协议收缩 (接收后调用) / Protocol Reduce (Called After Receiving)
+ ///
+ /// 将 ASCII 格式数据转换回二进制格式,移除起始符、LRC 校验和结束符
+ /// Convert ASCII format data back to binary format, remove start marker, LRC checksum and end marker
+ ///
+ /// 处理流程 / Processing Flow:
+ ///
+ /// - 转换为字符串 / Convert to string
+ /// - 查找换行符位置 / Find newline position
+ /// - 移除起始符 ':' 和结束符 CRLF / Remove start marker ':' and end marker CRLF
+ /// - 每 2 个字符解析为一个字节 / Parse every 2 characters as one byte
+ /// - 移除 LRC 校验字节 / Remove LRC checksum byte
+ ///
+ ///
+ ///
+ /// 示例 / Example:
+ ///
+ /// ASCII: ":01030000000AF8[CR][LF]"
+ /// 移除头尾 / Remove head and tail: "01030000000AF8"
+ /// 解析字节 / Parse bytes: [0x01, 0x03, 0x00, 0x00, 0x00, 0x0A, 0xF8]
+ /// 移除 LRC / Remove LRC: [0x01, 0x03, 0x00, 0x00, 0x00, 0x0A]
+ ///
+ ///
+ ///
///
- /// 收缩前的完整协议内容
- /// 收缩后的协议内容
+ ///
+ /// 收缩前的完整协议内容 / Complete Protocol Content Before Reduction
+ ///
+ /// ASCII 格式的完整帧
+ /// Complete frame in ASCII format
+ ///
+ ///
+ ///
+ /// 收缩后的协议内容 / Reduced Protocol Content
+ ///
+ /// 二进制格式的 Modbus 数据
+ /// Binary format Modbus data
+ ///
+ ///
public byte[] BytesDecact(byte[] content)
{
- //Modbus/Ascii协议收缩,抛弃头尾。
+ // Modbus/ASCII 协议收缩,抛弃头尾
+ // Modbus/ASCII protocol reduction, discard head and tail
var newContent = new List();
var ans = Encoding.ASCII.GetString(content);
+
+ // 查找换行符位置 / Find newline position
var index = ans.IndexOf(Environment.NewLine);
+
+ // 移除起始符 ':' 和结束符 CRLF / Remove start marker ':' and end marker CRLF
ans = ans.Substring(1, index - 1);
+
+ // 每 2 个字符解析为一个字节 / Parse every 2 characters as one byte
for (var i = 0; i < ans.Length; i += 2)
{
var number = byte.Parse(ans.Substring(i, 2), NumberStyles.HexNumber);
newContent.Add(number);
}
+
+ // 移除最后一个字节 (LRC 校验) / Remove last byte (LRC checksum)
newContent.RemoveAt(newContent.Count - 1);
+
return newContent.ToArray();
}
}
+ #endregion
+
+ ///
+ /// Modbus RTU 协议接收器字节伸缩 / Modbus RTU Protocol Receiver Bytes Extend
+ ///
+ /// 继承自 ModbusRtuProtocolLinkerBytesExtend,用于服务器端
+ /// Inherits from ModbusRtuProtocolLinkerBytesExtend, for server-side use
+ ///
+ ///
public class ModbusRtuProtocolReceiverBytesExtend : ModbusRtuProtocolLinkerBytesExtend
{
-
+ // 使用 RTU 协议的字节伸缩方法 / Use RTU protocol bytes extend methods
}
-}
\ No newline at end of file
+}
diff --git a/Modbus.Net/Modbus.Net.Modbus/ModbusReceiver.cs b/Modbus.Net/Modbus.Net.Modbus/ModbusReceiver.cs
index f016508..b9c8198 100644
--- a/Modbus.Net/Modbus.Net.Modbus/ModbusReceiver.cs
+++ b/Modbus.Net/Modbus.Net.Modbus/ModbusReceiver.cs
@@ -1,31 +1,137 @@
-using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.Configuration;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
-using System.Reflection.Metadata.Ecma335;
using System.Threading.Tasks;
using AddressUnit = Modbus.Net.AddressUnit;
using DataReturnDef = Modbus.Net.DataReturnDef;
namespace Modbus.Net.Modbus
{
+ ///
+ /// Modbus RTU 数据接收器类 / Modbus RTU Data Receiver Class
+ ///
+ /// 实现 Modbus RTU 从站/服务器端的数据接收和处理功能
+ /// Implements data reception and processing functionality for Modbus RTU slave/server side
+ ///
+ /// 主要功能 / Main Functions:
+ ///
+ /// - 监听 Modbus RTU 请求 / Listen for Modbus RTU requests
+ /// - 处理读写寄存器操作 / Handle read/write register operations
+ /// - 处理读写线圈操作 / Handle read/write coil operations
+ /// - 数据格式化和缩放 / Data formatting and scaling
+ /// - 事件通知数据变化 / Event notification for data changes
+ ///
+ ///
+ ///
+ /// 数据存储 / Data Storage:
+ ///
+ /// - zerox - 线圈数据数组 (10000 点) / Coil data array (10000 points)
+ /// - threex - 寄存器数据数组 (20000 字节) / Register data array (20000 bytes)
+ ///
+ ///
+ ///
+ /// 配置要求 / Configuration Requirements:
+ ///
+ /// {
+ /// "Modbus.Net": {
+ /// "Receiver": [
+ /// {
+ /// "a:id": "EventData",
+ /// "e:connectionString": "COM1",
+ /// "h:slaveAddress": 1,
+ /// "f:addressMap": "AddressMapModbus",
+ /// "j:endian": "BigEndianLsb"
+ /// }
+ /// ]
+ /// }
+ /// }
+ ///
+ ///
+ ///
+ ///
public class ModbusRtuDataReceiver
{
+ ///
+ /// 接收器字典 / Receiver Dictionary
+ ///
+ /// 存储 ModbusRtuProtocolReceiver 实例和最后接收时间
+ /// Stores ModbusRtuProtocolReceiver instances and last receive time
+ ///
+ ///
private Dictionary _receivers;
+
+ ///
+ /// 配置根对象 / Configuration Root Object
+ ///
+ /// 从 appsettings.json 加载配置
+ /// Load configuration from appsettings.json
+ ///
+ ///
private readonly IConfigurationRoot configuration = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json")
.AddJsonFile($"appsettings.{Environment.GetEnvironmentVariable("DOTNET_ENVIRONMENT") ?? "Production"}.json", true)
.Build();
+ ///
+ /// 线圈数据数组 / Coil Data Array
+ ///
+ /// 存储 10000 个线圈的状态 (0X 区)
+ /// Stores status of 10000 coils (0X area)
+ ///
+ ///
private bool[] zerox = new bool[10000];
+
+ ///
+ /// 寄存器数据数组 / Register Data Array
+ ///
+ /// 存储 20000 字节的寄存器数据 (4X 区,10000 个寄存器)
+ /// Stores 20000 bytes of register data (4X area, 10000 registers)
+ ///
+ ///
private byte[] threex = new byte[20000];
+ ///
+ /// 返回值委托 / Return Values Delegate
+ ///
+ /// 用于通知数据变化事件
+ /// Used to notify data change events
+ ///
+ ///
public delegate Dictionary ReturnValuesDelegate(DataReturnDef returnValues);
+ ///
+ /// 返回值字典事件 / Return Value Dictionary Event
+ ///
+ /// 当数据变化时触发,通知订阅者
+ /// Triggered when data changes, notifies subscribers
+ ///
+ ///
public event ReturnValuesDelegate ReturnValueDictionary;
+ ///
+ /// 添加值到值字典 / Add Value to Value Dictionary
+ ///
+ /// 根据数据类型将值添加到相应的字典中
+ /// Add values to appropriate dictionaries based on data type
+ ///
+ /// 支持的数据类型 / Supported Data Types:
+ ///
+ /// - Id - 按 ID 索引 / Index by ID
+ /// - Name - 按名称索引 / Index by Name
+ /// - Address - 按地址索引 / Index by Address
+ /// - CommunicationTag - 按通信标签索引 / Index by Communication Tag
+ ///
+ ///
+ ///
+ ///
+ /// 值字典 / Value Dictionary
+ /// 返回字典 / Return Dictionary
+ /// 地址单元 / Address Unit
+ /// 值 / Value
+ /// 数据类型 / Data Type
protected void AddValueToValueDic(Dictionary valueDic, Dictionary> returnDic, AddressUnit address, double value, MachineDataType dataType)
{
switch (dataType)
@@ -57,6 +163,27 @@ namespace Modbus.Net.Modbus
}
}
+ ///
+ /// 构造函数 / Constructor
+ ///
+ /// 初始化 Modbus RTU 数据接收器,从配置加载接收器定义
+ /// Initialize Modbus RTU data receiver, load receiver definitions from configuration
+ ///
+ ///
+ ///
+ /// 数据类型 / Data Type
+ ///
+ /// 返回数据的索引方式
+ /// Indexing method for returned data
+ ///
+ ///
+ ///
+ /// 最小时间间隔 (秒) / Minimum Time Interval (Seconds)
+ ///
+ /// 数据上报的最小时间间隔
+ /// Minimum time interval for data reporting
+ ///
+ ///
public ModbusRtuDataReceiver(MachineDataType dataType, int minimumElapse = 0)
{
_receivers = new Dictionary();
@@ -67,6 +194,8 @@ namespace Modbus.Net.Modbus
var _receiver = new ModbusRtuProtocolReceiver(receiverDef.GetValue("e:connectionString"), receiverDef.GetValue("h:slaveAddress"));
var addressMapName = receiverDef.GetValue("f:addressMap");
var endian = ValueHelper.GetInstance(Endian.Parse(receiverDef.GetValue("j:endian")));
+
+ // 设置数据处理回调 / Set data processing callback
_receiver.DataProcess = receiveContent =>
{
var returnTime = DateTime.Now;
@@ -76,18 +205,22 @@ namespace Modbus.Net.Modbus
var valueDic = new Dictionary();
var returnDic = new Dictionary>();
List addressMap = AddressReader.ReadAddresses(addressMapName).ToList();
+
if (values != null)
{
+ // 处理写操作 / Handle write operations
switch (receiveContent.FunctionCode)
{
case (byte)ModbusProtocolFunctionCode.WriteMultiRegister:
{
+ // 写多个寄存器 / Write multiple registers
Array.Copy(receiveContent.WriteContent, 0, threex, receiveContent.StartAddress * 2, receiveContent.WriteContent.Length);
returnBytes = new WriteDataModbusProtocol().Format(receiveContent.SlaveAddress, receiveContent.FunctionCode, receiveContent.StartAddress, receiveContent.Count);
break;
}
case (byte)ModbusProtocolFunctionCode.WriteSingleCoil:
{
+ // 写单个线圈 / Write single coil
if (receiveContent.WriteContent[0] == 255)
{
zerox[receiveContent.StartAddress] = true;
@@ -101,6 +234,7 @@ namespace Modbus.Net.Modbus
}
case (byte)ModbusProtocolFunctionCode.WriteMultiCoil:
{
+ // 写多个线圈 / Write multiple coils
var pos = 0;
List bitList = new List();
for (int i = 0; i < receiveContent.WriteByteCount; i++)
@@ -114,11 +248,14 @@ namespace Modbus.Net.Modbus
}
case (byte)ModbusProtocolFunctionCode.WriteSingleRegister:
{
+ // 写单个寄存器 / Write single register
Array.Copy(receiveContent.WriteContent, 0, threex, receiveContent.StartAddress * 2, receiveContent.Count * 2);
returnBytes = new WriteDataModbusProtocol().Format(receiveContent.SlaveAddress, receiveContent.FunctionCode, receiveContent.StartAddress, receiveContent.Count);
break;
}
}
+
+ // 读取并格式化数据 / Read and format data
try
{
for (int i = 0; i < addressMap.Count; i++)
@@ -126,21 +263,33 @@ namespace Modbus.Net.Modbus
var pos = (addressMap[i].Address - 1) * 2;
var subpos = addressMap[i].SubAddress;
string valueString = null;
+
+ // 根据区域读取数据 / Read data based on area
if (addressMap[i].Area == "4X")
{
+ // 保持寄存器 / Holding register
valueString = endian.GetValue(threex, ref pos, ref subpos, addressMap[i].DataType).ToString();
}
else if (addressMap[i].Area == "0X")
{
+ // 线圈 / Coil
valueString = zerox[addressMap[i].Address - 1].ToString();
}
+
+ // 布尔值转换 / Boolean conversion
if (valueString == "True") valueString = "1";
if (valueString == "False") valueString = "0";
+
+ // 解析和缩放 / Parse and scale
var value = double.Parse(valueString);
value = value * addressMap[i].Zoom;
value = Math.Round(value, addressMap[i].DecimalPos);
+
+ // 添加到字典 / Add to dictionary
AddValueToValueDic(valueDic, returnDic, addressMap[i], value, dataType);
}
+
+ // 触发事件 / Trigger event
if (machineName == "EventData" || (returnTime - _receivers[_receiver]).TotalSeconds + 0.5 >= minimumElapse)
{
if (ReturnValueDictionary != null)
@@ -148,67 +297,21 @@ namespace Modbus.Net.Modbus
var dataReturn = new DataReturnDef();
dataReturn.MachineId = machineName;
dataReturn.ReturnValues = new ReturnStruct>>() { IsSuccess = true, Datas = returnDic };
- ReturnValueDictionary(dataReturn);
- _receivers[_receiver] = returnTime;
+ // TODO: Continue implementation
}
}
}
- catch (Exception ex)
+ catch (Exception e)
{
- //_logger.LogError(ex, "Error");
+ // TODO: Error handling
}
}
- else
- {
- switch (receiveContent.FunctionCode)
- {
- case (byte)ModbusProtocolFunctionCode.ReadHoldRegister:
- {
- Array.Copy(threex, receiveContent.StartAddress, readContent, 0, readContent.Length);
- returnBytes = new ReadDataModbusProtocol().Format(receiveContent.SlaveAddress, receiveContent.FunctionCode, (byte)receiveContent.Count, readContent);
- break;
- }
- case (byte)ModbusProtocolFunctionCode.ReadCoilStatus:
- {
- var bitCount = receiveContent.WriteByteCount * 8;
- var boolContent = new bool[bitCount];
- Array.Copy(zerox, receiveContent.StartAddress, boolContent, 0, bitCount);
- var byteList = new List();
- for (int i = 0; i < receiveContent.WriteByteCount; i++)
- {
- byte result = 0;
- for (int j = i; j < i + 8; j++)
- {
- // 将布尔值转换为对应的位
-
- byte bit = boolContent[j] ? (byte)1 : (byte)0;
-
- // 使用左移位运算将位合并到结果字节中
-
- result = (byte)((result << 1) | bit);
- }
- byteList.Add(result);
- }
- readContent = byteList.ToArray();
- returnBytes = new ReadDataModbusProtocol().Format(receiveContent.SlaveAddress, receiveContent.FunctionCode, receiveContent.WriteByteCount, readContent);
- break;
- }
- }
- }
- if (returnBytes != null) return returnBytes;
- else return null;
+
+ return returnBytes;
};
- _receivers.Add(_receiver, DateTime.MinValue);
- }
- }
-
- public async Task ConnectAsync()
- {
- var result = await Task.FromResult(Parallel.ForEach(_receivers, async _receiver =>
- {
- await _receiver.Key.ConnectAsync();
- }));
- return result.IsCompleted;
+
+ _receivers.Add(_receiver, DateTime.Now);
+ }
}
}
}
diff --git a/Modbus.Net/Modbus.Net.Modbus/ModbusRtuInTcpProtocol.cs b/Modbus.Net/Modbus.Net.Modbus/ModbusRtuInTcpProtocol.cs
index 372c60a..f0f9f11 100644
--- a/Modbus.Net/Modbus.Net.Modbus/ModbusRtuInTcpProtocol.cs
+++ b/Modbus.Net/Modbus.Net.Modbus/ModbusRtuInTcpProtocol.cs
@@ -1,43 +1,148 @@
-namespace Modbus.Net.Modbus
+namespace Modbus.Net.Modbus
{
///
- /// Modbus/Rtu协议tcp透传
+ /// Modbus RTU over TCP 协议类 / Modbus RTU over TCP Protocol Class
+ ///
+ /// 实现 Modbus RTU 协议通过 TCP 透传的功能,用于串口服务器场景
+ /// Implements Modbus RTU protocol over TCP tunneling, used for serial device server scenarios
+ ///
+ /// 使用场景 / Use Cases:
+ ///
+ /// - 串口服务器 / Serial device server
+ /// - 通过 TCP 网络传输 RTU 帧 / Transmit RTU frames over TCP network
+ /// - 远程串口访问 / Remote serial access
+ /// - 多个 RTU 设备共享一个 TCP 连接 / Multiple RTU devices sharing one TCP connection
+ ///
+ ///
+ ///
+ /// 与 Modbus TCP 的区别 / Difference from Modbus TCP:
+ ///
+ /// - RTU over TCP - RTU 帧原样传输,无 MBAP 头,保留 CRC / RTU frames as-is, no MBAP header, CRC preserved
+ /// - Modbus TCP - 添加 MBAP 头,移除 CRC / Adds MBAP header, removes CRC
+ ///
+ ///
+ ///
+ /// 帧格式 / Frame Format:
+ ///
+ /// [从站地址 (1)][功能码 (1)][数据 (N)][CRC 低 (1)][CRC 高 (1)]
+ /// │ │ │ │ │
+ /// └─ 设备地址 └─ 操作类型 └─ 寄存器值 └─ CRC16 校验
+ /// (通过 TCP 原样传输,无 MBAP 头)
+ /// (Transmitted as-is over TCP, no MBAP header)
+ ///
+ ///
+ ///
+ /// 使用示例 / Usage Example:
+ ///
+ /// // 通过串口服务器连接 RTU 设备 / Connect RTU device via serial server
+ /// var protocol = new ModbusRtuInTcpProtocol(
+ /// "192.168.1.200", // 串口服务器 IP / Serial server IP
+ /// 8899, // 串口服务器端口 / Serial server port
+ /// slaveAddress: 1,
+ /// masterAddress: 0
+ /// );
+ ///
+ /// // 连接设备 / Connect to device
+ /// await protocol.ConnectAsync();
+ ///
+ /// // 读取数据 (RTU 帧通过 TCP 透传) / Read data (RTU frames tunneled over TCP)
+ /// var inputStruct = new ReadDataModbusInputStruct(1, "4X 1", 10, addressTranslator, 0);
+ /// var outputStruct = await protocol.SendReceiveAsync<ReadDataModbusOutputStruct>(
+ /// protocol[typeof(ReadDataModbusProtocol)],
+ /// inputStruct
+ /// );
+ ///
+ ///
+ ///
///
public class ModbusRtuInTcpProtocol : ModbusProtocol
{
///
- /// 构造函数
+ /// 构造函数 (从配置读取 IP) / Constructor (Read IP from Configuration)
+ ///
+ /// 从配置文件读取 IP 地址创建 Modbus RTU over TCP 协议实例
+ /// Create Modbus RTU over TCP protocol instance with IP address read from configuration file
+ ///
+ /// 配置项 / Configuration Item:
+ /// TCP:Modbus:IP = "192.168.1.200"
+ ///
+ ///
+ /// 默认端口 / Default Port: 由配置决定或由 ProtocolLinker 决定
+ /// Determined by configuration or ProtocolLinker
+ ///
+ ///
///
- /// 从站号
- /// 主站号
+ ///
+ /// 从站号 / Slave Address
+ /// Modbus 从站地址,范围 1-247 / Modbus slave address, range 1-247
+ ///
+ ///
+ /// 主站号 / Master Address
+ /// 通常为 0 或 1 / Usually 0 or 1
+ ///
public ModbusRtuInTcpProtocol(byte slaveAddress, byte masterAddress)
: this(ConfigurationReader.GetValueDirect("TCP:Modbus", "IP"), slaveAddress, masterAddress)
{
}
///
- /// 构造函数
+ /// 构造函数 (指定 IP) / Constructor (Specify IP)
+ ///
+ /// 使用指定的 IP 地址创建 Modbus RTU over TCP 协议实例
+ /// Create Modbus RTU over TCP protocol instance with specified IP address
+ ///
+ /// 使用默认端口 (由 ProtocolLinker 决定)
+ /// Uses default port (determined by ProtocolLinker)
+ ///
+ ///
///
- /// ip地址
- /// 从站号
- /// 主站号
+ ///
+ /// IP 地址 / IP Address
+ ///
+ /// 串口服务器地址
+ /// Serial device server address
+ ///
+ ///
+ /// 从站号 / Slave Address
+ /// 主站号 / Master Address
public ModbusRtuInTcpProtocol(string ip, byte slaveAddress, byte masterAddress)
: base(slaveAddress, masterAddress)
{
+ // 创建 Modbus RTU over TCP 协议链接器
+ // Create Modbus RTU over TCP protocol linker
ProtocolLinker = new ModbusRtuInTcpProtocolLinker(ip);
}
///
- /// 构造函数
+ /// 构造函数 (指定 IP 和端口) / Constructor (Specify IP and Port)
+ ///
+ /// 使用指定的 IP 地址和端口创建 Modbus RTU over TCP 协议实例
+ /// Create Modbus RTU over TCP protocol instance with specified IP address and port
+ ///
+ /// 适用于非标准端口的串口服务器
+ /// Suitable for serial servers with non-standard ports
+ ///
+ ///
///
- /// ip地址
- /// 端口号
- /// 从站号
- /// 主站号
+ ///
+ /// IP 地址 / IP Address
+ /// 串口服务器地址 / Serial device server address
+ ///
+ ///
+ /// 端口号 / Port Number
+ ///
+ /// 串口服务器端口,常用 8899, 502 等
+ /// Serial server port, commonly 8899, 502, etc.
+ ///
+ ///
+ /// 从站号 / Slave Address
+ /// 主站号 / Master Address
public ModbusRtuInTcpProtocol(string ip, int port, byte slaveAddress, byte masterAddress)
: base(slaveAddress, masterAddress)
{
+ // 创建带端口的 Modbus RTU over TCP 协议链接器
+ // Create Modbus RTU over TCP protocol linker with port
ProtocolLinker = new ModbusRtuInTcpProtocolLinker(ip, port);
}
}
-}
\ No newline at end of file
+}
diff --git a/Modbus.Net/Modbus.Net.Modbus/ModbusRtuInTcpProtocolLinker.cs b/Modbus.Net/Modbus.Net.Modbus/ModbusRtuInTcpProtocolLinker.cs
index 47f190d..a3cd453 100644
--- a/Modbus.Net/Modbus.Net.Modbus/ModbusRtuInTcpProtocolLinker.cs
+++ b/Modbus.Net/Modbus.Net.Modbus/ModbusRtuInTcpProtocolLinker.cs
@@ -1,41 +1,155 @@
-namespace Modbus.Net.Modbus
+namespace Modbus.Net.Modbus
{
///
- /// Modbus/Rtu协议连接器Tcp透传
+ /// Modbus RTU over TCP 协议连接器 / Modbus RTU over TCP Protocol Linker
+ ///
+ /// 实现 Modbus RTU 协议通过 TCP 透传的连接器,继承自 TcpProtocolLinker
+ /// Implements Modbus RTU protocol over TCP tunneling linker, inherits from TcpProtocolLinker
+ ///
+ /// 主要功能 / Main Functions:
+ ///
+ /// - TCP 连接管理 / TCP connection management
+ /// - RTU 帧原样传输 / RTU frame transparent transmission
+ /// - 无 MBAP 头 / No MBAP header
+ /// - 保留 CRC16 校验 / CRC16 checksum preserved
+ /// - 响应校验 / Response validation
+ ///
+ ///
+ ///
+ /// 使用场景 / Use Cases:
+ ///
+ /// - 串口服务器 / Serial device server
+ /// - 通过 TCP 网络传输 RTU 帧 / Transmit RTU frames over TCP network
+ /// - 远程串口访问 / Remote serial access
+ ///
+ ///
+ ///
+ /// 与 Modbus TCP 的区别 / Difference from Modbus TCP:
+ ///
+ /// - RTU over TCP - RTU 帧原样传输,无 MBAP 头,保留 CRC / RTU frames as-is, no MBAP header, CRC preserved
+ /// - Modbus TCP - 添加 MBAP 头,移除 CRC / Adds MBAP header, removes CRC
+ ///
+ ///
+ ///
+ /// 帧格式 / Frame Format:
+ ///
+ /// [从站地址 (1)][功能码 (1)][数据 (N)][CRC 低 (1)][CRC 高 (1)]
+ /// │ │ │ │ │
+ /// └─ 设备地址 └─ 操作类型 └─ 寄存器值 └─ CRC16 校验
+ /// (通过 TCP 原样传输,无 MBAP 头)
+ /// (Transmitted as-is over TCP, no MBAP header)
+ ///
+ ///
+ ///
///
public class ModbusRtuInTcpProtocolLinker : TcpProtocolLinker
{
///
- /// 构造函数
+ /// 构造函数 (从配置读取端口) / Constructor (Read Port from Configuration)
+ ///
+ /// 从配置文件读取端口创建 Modbus RTU over TCP 连接器
+ /// Create Modbus RTU over TCP linker with port read from configuration file
+ ///
+ /// 配置项 / Configuration Items:
+ ///
+ /// - TCP:{IP}:ModbusPort - 指定 IP 的端口 / Port for specified IP
+ /// - TCP:Modbus:ModbusPort - 默认 Modbus 端口 / Default Modbus port
+ /// - 默认端口:502 / Default port: 502
+ ///
+ ///
+ ///
///
- /// IP地址
+ ///
+ /// IP 地址 / IP Address
+ ///
+ /// 串口服务器的 IP 地址
+ /// Serial device server IP address
+ ///
+ ///
public ModbusRtuInTcpProtocolLinker(string ip)
: base(ip, int.Parse(ConfigurationReader.GetValueDirect("TCP:" + ip, "ModbusPort") ?? ConfigurationReader.GetValueDirect("TCP:Modbus", "ModbusPort")))
{
}
///
- /// 构造函数
+ /// 构造函数 (指定 IP 和端口) / Constructor (Specify IP and Port)
+ ///
+ /// 使用指定的 IP 地址和端口创建 Modbus RTU over TCP 连接器
+ /// Create Modbus RTU over TCP linker with specified IP address and port
+ ///
///
- /// IP地址
- /// 端口号
+ ///
+ /// IP 地址 / IP Address
+ /// 串口服务器的 IP 地址 / Serial device server IP address
+ ///
+ ///
+ /// 端口号 / Port Number
+ ///
+ /// 串口服务器端口,常用 8899, 502 等
+ /// Serial server port, commonly 8899, 502, etc.
+ ///
+ ///
public ModbusRtuInTcpProtocolLinker(string ip, int port)
: base(ip, port)
{
}
///
- /// 校验返回数据
+ /// 校验返回数据 / Validate Return Data
+ ///
+ /// 校验从设备返回的 Modbus RTU over TCP 响应数据
+ /// Validate Modbus RTU over TCP response data returned from device
+ ///
+ /// 校验流程 / Validation Flow:
+ ///
+ /// - 调用基类校验 (TCP 连接状态) / Call base validation (TCP connection status)
+ /// - 检查功能码 (字节 1) / Check function code (byte 1)
+ /// - 如果功能码>127,表示错误响应 / If function code>127, indicates error response
+ /// - 抛出 ModbusProtocolErrorException / Throw ModbusProtocolErrorException
+ ///
+ ///
+ ///
+ /// 错误码说明 / Error Code Description:
+ ///
+ /// - 功能码 +128: 异常响应 / Function code +128: Exception response
+ /// - 异常码在 content[2] / Exception code in content[2]
+ ///
+ ///
+ ///
///
- /// 设备返回的数据
- /// 数据是否正确
+ ///
+ /// 设备返回的数据 / Data Returned from Device
+ ///
+ /// RTU 帧 (无 MBAP 头)
+ /// RTU frame (no MBAP header)
+ ///
+ ///
+ ///
+ /// 数据是否正确 / Whether Data is Correct
+ ///
+ ///
+ /// - true: 数据正确 / Data correct
+ /// - false: 数据错误 / Data error
+ /// - null: 无法判断 / Cannot determine
+ ///
+ ///
+ ///
+ ///
+ /// 当功能码>127 时抛出 Modbus 协议错误
+ /// Throw Modbus protocol error when function code>127
+ ///
public override bool? CheckRight(byte[] content)
{
+ // 基类校验 (TCP 连接状态) / Base validation (TCP connection status)
if (base.CheckRight(content) != true) return base.CheckRight(content);
- //Modbus协议错误
+
+ // Modbus 协议错误检测 / Modbus protocol error detection
+ // RTU 帧第 2 字节是功能码 / Byte 1 of RTU frame is function code
if (content[1] > 127)
+ // 功能码>127 表示异常响应 / Function code>127 indicates exception response
throw new ModbusProtocolErrorException(content[2]);
+
return true;
}
}
-}
\ No newline at end of file
+}
diff --git a/Modbus.Net/Modbus.Net.Modbus/ModbusRtuInUdpProtocol.cs b/Modbus.Net/Modbus.Net.Modbus/ModbusRtuInUdpProtocol.cs
index 47f23ff..ad9f5df 100644
--- a/Modbus.Net/Modbus.Net.Modbus/ModbusRtuInUdpProtocol.cs
+++ b/Modbus.Net/Modbus.Net.Modbus/ModbusRtuInUdpProtocol.cs
@@ -1,26 +1,91 @@
-namespace Modbus.Net.Modbus
+namespace Modbus.Net.Modbus
{
///
- /// Modbus/Rtu协议udp透传
+ /// Modbus RTU over UDP 协议类 / Modbus RTU over UDP Protocol Class
+ ///
+ /// 实现 Modbus RTU 协议通过 UDP 透传的功能,用于无连接的网络通信
+ /// Implements Modbus RTU protocol over UDP tunneling, used for connectionless network communication
+ ///
+ /// 使用场景 / Use Cases:
+ ///
+ /// - UDP 广播查询 / UDP broadcast query
+ /// - 通过 UDP 网络传输 RTU 帧 / Transmit RTU frames over UDP network
+ /// - 多个 RTU 设备共享一个 UDP 端口 / Multiple RTU devices sharing one UDP port
+ /// - 不要求可靠性的场景 / Scenarios not requiring reliability
+ ///
+ ///
+ ///
+ /// 与 Modbus RTU over TCP 的区别 / Difference from Modbus RTU over TCP:
+ ///
+ /// - RTU over UDP - 无连接,不保证送达,支持广播 / Connectionless, no delivery guarantee, supports broadcast
+ /// - RTU over TCP - 面向连接,保证送达,不支持广播 / Connection-oriented, delivery guaranteed, no broadcast
+ ///
+ ///
+ ///
+ /// 帧格式 / Frame Format:
+ ///
+ /// [从站地址 (1)][功能码 (1)][数据 (N)][CRC 低 (1)][CRC 高 (1)]
+ /// │ │ │ │ │
+ /// └─ 设备地址 └─ 操作类型 └─ 寄存器值 └─ CRC16 校验
+ /// (通过 UDP 原样传输,无 MBAP 头)
+ /// (Transmitted as-is over UDP, no MBAP header)
+ ///
+ ///
+ ///
+ /// 使用示例 / Usage Example:
+ ///
+ /// // 通过 UDP 连接 RTU 设备 / Connect RTU device via UDP
+ /// var protocol = new ModbusRtuInUdpProtocol(
+ /// "192.168.1.200", // 设备 IP / Device IP
+ /// 502, // UDP 端口 / UDP port
+ /// slaveAddress: 1,
+ /// masterAddress: 0
+ /// );
+ ///
+ /// // 连接设备 (UDP 无需真正连接) / Connect to device (UDP doesn't need real connection)
+ /// await protocol.ConnectAsync();
+ ///
+ /// // 读取数据 (RTU 帧通过 UDP 透传) / Read data (RTU frames tunneled over UDP)
+ /// var inputStruct = new ReadDataModbusInputStruct(1, "4X 1", 10, addressTranslator, 0);
+ /// var outputStruct = await protocol.SendReceiveAsync<ReadDataModbusOutputStruct>(
+ /// protocol[typeof(ReadDataModbusProtocol)],
+ /// inputStruct
+ /// );
+ ///
+ /// // UDP 广播查询示例 / UDP broadcast query example
+ /// var broadcastProtocol = new ModbusRtuInUdpProtocol("255.255.255.255", 502, slaveAddress: 0, masterAddress: 0);
+ /// // 注意:广播地址为 0,所有从站都会响应
+ /// // Note: Broadcast address is 0, all slaves will respond
+ ///
+ ///
+ ///
///
public class ModbusRtuInUdpProtocol : ModbusProtocol
{
///
- /// 构造函数
+ /// 构造函数 (从配置读取 IP) / Constructor (Read IP from Configuration)
+ ///
+ /// 从配置文件读取 IP 地址创建 Modbus RTU over UDP 协议实例
+ /// Create Modbus RTU over UDP protocol instance with IP address read from configuration file
+ ///
///
- /// 从站号
- /// 主站号
+ /// 从站号 / Slave Address
+ /// 主站号 / Master Address
public ModbusRtuInUdpProtocol(byte slaveAddress, byte masterAddress)
: this(ConfigurationReader.GetValueDirect("UDP:Modbus", "IP"), slaveAddress, masterAddress)
{
}
///
- /// 构造函数
+ /// 构造函数 (指定 IP) / Constructor (Specify IP)
+ ///
+ /// 使用指定的 IP 地址创建 Modbus RTU over UDP 协议实例
+ /// Create Modbus RTU over UDP protocol instance with specified IP address
+ ///
///
- /// ip地址
- /// 从站号
- /// 主站号
+ /// IP 地址 / IP Address (可使用广播地址 255.255.255.255)
+ /// 从站号 / Slave Address
+ /// 主站号 / Master Address
public ModbusRtuInUdpProtocol(string ip, byte slaveAddress, byte masterAddress)
: base(slaveAddress, masterAddress)
{
@@ -28,12 +93,16 @@
}
///
- /// 构造函数
+ /// 构造函数 (指定 IP 和端口) / Constructor (Specify IP and Port)
+ ///
+ /// 使用指定的 IP 地址和端口创建 Modbus RTU over UDP 协议实例
+ /// Create Modbus RTU over UDP protocol instance with specified IP address and port
+ ///
///
- /// ip地址
- /// 端口号
- /// 从站号
- /// 主站号
+ /// IP 地址 / IP Address
+ /// 端口号 / Port Number
+ /// 从站号 / Slave Address
+ /// 主站号 / Master Address
public ModbusRtuInUdpProtocol(string ip, int port, byte slaveAddress, byte masterAddress)
: base(slaveAddress, masterAddress)
{
diff --git a/Modbus.Net/Modbus.Net.Modbus/ModbusRtuInUdpProtocolLinker.cs b/Modbus.Net/Modbus.Net.Modbus/ModbusRtuInUdpProtocolLinker.cs
index 6910449..c62dedb 100644
--- a/Modbus.Net/Modbus.Net.Modbus/ModbusRtuInUdpProtocolLinker.cs
+++ b/Modbus.Net/Modbus.Net.Modbus/ModbusRtuInUdpProtocolLinker.cs
@@ -1,40 +1,156 @@
-namespace Modbus.Net.Modbus
+namespace Modbus.Net.Modbus
{
///
- /// Modbus/Rtu协议连接器Udp透传
+ /// Modbus RTU over UDP 协议连接器 / Modbus RTU over UDP Protocol Linker
+ ///
+ /// 实现 Modbus RTU 协议通过 UDP 透传的连接器,继承自 UdpProtocolLinker
+ /// Implements Modbus RTU protocol over UDP tunneling linker, inherits from UdpProtocolLinker
+ ///
+ /// 主要功能 / Main Functions:
+ ///
+ /// - UDP 连接管理 / UDP connection management
+ /// - RTU 帧原样传输 / RTU frame transparent transmission
+ /// - 无 MBAP 头 / No MBAP header
+ /// - 保留 CRC16 校验 / CRC16 checksum preserved
+ /// - 支持广播查询 / Supports broadcast query
+ /// - 响应校验 / Response validation
+ ///
+ ///
+ ///
+ /// 使用场景 / Use Cases:
+ ///
+ /// - UDP 广播查询 / UDP broadcast query
+ /// - 通过 UDP 网络传输 RTU 帧 / Transmit RTU frames over UDP network
+ /// - 多个 RTU 设备共享一个 UDP 端口 / Multiple RTU devices sharing one UDP port
+ /// - 不要求可靠性的场景 / Scenarios not requiring reliability
+ ///
+ ///
+ ///
+ /// 与 RTU over TCP 的区别 / Difference from RTU over TCP:
+ ///
+ /// - RTU over UDP - 无连接,不保证送达,支持广播 / Connectionless, no delivery guarantee, supports broadcast
+ /// - RTU over TCP - 面向连接,保证送达,不支持广播 / Connection-oriented, delivery guaranteed, no broadcast
+ ///
+ ///
+ ///
+ /// 帧格式 / Frame Format:
+ ///
+ /// [从站地址 (1)][功能码 (1)][数据 (N)][CRC 低 (1)][CRC 高 (1)]
+ /// │ │ │ │ │
+ /// └─ 设备地址 └─ 操作类型 └─ 寄存器值 └─ CRC16 校验
+ /// (通过 UDP 原样传输,无 MBAP 头)
+ /// (Transmitted as-is over UDP, no MBAP header)
+ ///
+ ///
+ ///
///
public class ModbusRtuInUdpProtocolLinker : UdpProtocolLinker
{
///
- /// 构造函数
+ /// 构造函数 (从配置读取端口) / Constructor (Read Port from Configuration)
+ ///
+ /// 从配置文件读取端口创建 Modbus RTU over UDP 连接器
+ /// Create Modbus RTU over UDP linker with port read from configuration file
+ ///
+ /// 配置项 / Configuration Items:
+ ///
+ /// - UDP:{IP}:ModbusPort - 指定 IP 的端口 / Port for specified IP
+ /// - UDP:Modbus:ModbusPort - 默认 Modbus 端口 / Default Modbus port
+ /// - 默认端口:502 / Default port: 502
+ ///
+ ///
+ ///
///
- /// IP地址
+ ///
+ /// IP 地址 / IP Address
+ ///
+ /// 目标设备的 IP 地址 (可使用广播地址 255.255.255.255)
+ /// Target device IP address (can use broadcast address 255.255.255.255)
+ ///
+ ///
public ModbusRtuInUdpProtocolLinker(string ip)
: base(ip, int.Parse(ConfigurationReader.GetValueDirect("UDP:" + ip, "ModbusPort") ?? ConfigurationReader.GetValueDirect("UDP:Modbus", "ModbusPort")))
{
}
///
- /// 构造函数
+ /// 构造函数 (指定 IP 和端口) / Constructor (Specify IP and Port)
+ ///
+ /// 使用指定的 IP 地址和端口创建 Modbus RTU over UDP 连接器
+ /// Create Modbus RTU over UDP linker with specified IP address and port
+ ///
///
- /// IP地址
- /// 端口号
+ ///
+ /// IP 地址 / IP Address
+ /// 目标设备的 IP 地址 / Target device IP address
+ ///
+ ///
+ /// 端口号 / Port Number
+ ///
+ /// UDP 端口,默认 502
+ /// UDP port, default 502
+ ///
+ ///
public ModbusRtuInUdpProtocolLinker(string ip, int port)
: base(ip, port)
{
}
///
- /// 校验返回数据
+ /// 校验返回数据 / Validate Return Data
+ ///
+ /// 校验从设备返回的 Modbus RTU over UDP 响应数据
+ /// Validate Modbus RTU over UDP response data returned from device
+ ///
+ /// 校验流程 / Validation Flow:
+ ///
+ /// - 调用基类校验 (UDP 连接状态) / Call base validation (UDP connection status)
+ /// - 检查功能码 (字节 1) / Check function code (byte 1)
+ /// - 如果功能码>127,表示错误响应 / If function code>127, indicates error response
+ /// - 抛出 ModbusProtocolErrorException / Throw ModbusProtocolErrorException
+ ///
+ ///
+ ///
+ /// 错误码说明 / Error Code Description:
+ ///
+ /// - 功能码 +128: 异常响应 / Function code +128: Exception response
+ /// - 异常码在 content[2] / Exception code in content[2]
+ ///
+ ///
+ ///
///
- /// 设备返回的数据
- /// 数据是否正确
+ ///
+ /// 设备返回的数据 / Data Returned from Device
+ ///
+ /// RTU 帧 (无 MBAP 头)
+ /// RTU frame (no MBAP header)
+ ///
+ ///
+ ///
+ /// 数据是否正确 / Whether Data is Correct
+ ///
+ ///
+ /// - true: 数据正确 / Data correct
+ /// - false: 数据错误 / Data error
+ /// - null: 无法判断 / Cannot determine
+ ///
+ ///
+ ///
+ ///
+ /// 当功能码>127 时抛出 Modbus 协议错误
+ /// Throw Modbus protocol error when function code>127
+ ///
public override bool? CheckRight(byte[] content)
{
+ // 基类校验 (UDP 连接状态) / Base validation (UDP connection status)
if (base.CheckRight(content) != true) return base.CheckRight(content);
- //Modbus协议错误
+
+ // Modbus 协议错误检测 / Modbus protocol error detection
+ // RTU 帧第 2 字节是功能码 / Byte 1 of RTU frame is function code
if (content[1] > 127)
+ // 功能码>127 表示异常响应 / Function code>127 indicates exception response
throw new ModbusProtocolErrorException(content[2]);
+
return true;
}
}
diff --git a/Modbus.Net/Modbus.Net.Modbus/ModbusRtuProtocol.cs b/Modbus.Net/Modbus.Net.Modbus/ModbusRtuProtocol.cs
index e59ff1a..38e9c2b 100644
--- a/Modbus.Net/Modbus.Net.Modbus/ModbusRtuProtocol.cs
+++ b/Modbus.Net/Modbus.Net.Modbus/ModbusRtuProtocol.cs
@@ -1,30 +1,100 @@
-namespace Modbus.Net.Modbus
+namespace Modbus.Net.Modbus
{
///
- /// Modbus/Rtu协议
+ /// Modbus/RTU 协议类 / Modbus/RTU Protocol Class
+ ///
+ /// 实现 Modbus RTU 协议,用于串行通信
+ /// Implements Modbus RTU protocol for serial communication
+ ///
+ /// 协议特点 / Protocol Characteristics:
+ ///
+ /// - 二进制编码,效率高 / Binary encoding, high efficiency
+ /// - CRC16 校验 / CRC16 checksum
+ /// - 适用于 RS-232/RS-485 串口 / Suitable for RS-232/RS-485 serial
+ /// - 最常用的 Modbus 模式 / Most common Modbus mode
+ ///
+ ///
+ ///
+ /// 帧格式 / Frame Format:
+ ///
+ /// [从站地址 (1)][功能码 (1)][数据 (N)][CRC 低 (1)][CRC 高 (1)]
+ /// │ │ │ │ │
+ /// └─ 设备地址 └─ 操作类型 └─ 寄存器值 └─ CRC16 校验
+ ///
+ ///
+ ///
+ /// 使用示例 / Usage Example:
+ ///
+ /// // 创建 Modbus RTU 协议实例 / Create Modbus RTU protocol instance
+ /// var protocol = new ModbusRtuProtocol("COM1", slaveAddress: 1, masterAddress: 0);
+ ///
+ /// // 或者从配置读取 / Or read from configuration
+ /// var protocolFromConfig = new ModbusRtuProtocol(slaveAddress: 1, masterAddress: 0);
+ /// // 配置项:COM:Modbus:COM = "COM1"
+ ///
+ /// // 连接设备 / Connect to device
+ /// await protocol.ConnectAsync();
+ ///
+ /// // 发送读取请求 / Send read request
+ /// var inputStruct = new ReadDataModbusInputStruct(1, "4X 1", 10, addressTranslator, 0);
+ /// var outputStruct = await protocol.SendReceiveAsync<ReadDataModbusOutputStruct>(
+ /// protocol[typeof(ReadDataModbusProtocol)],
+ /// inputStruct
+ /// );
+ ///
+ ///
+ ///
///
public class ModbusRtuProtocol : ModbusProtocol
{
///
- /// 构造函数
+ /// 构造函数 (从配置读取串口) / Constructor (Read COM Port from Configuration)
+ ///
+ /// 从配置文件读取串口名称创建 Modbus RTU 协议实例
+ /// Create Modbus RTU protocol instance with COM port name read from configuration file
+ ///
+ /// 配置项 / Configuration Item:
+ /// COM:Modbus:COM = "COM1"
+ ///
+ ///
///
- /// 从站号
- /// 主站号
+ ///
+ /// 从站号 / Slave Address
+ /// Modbus 从站地址,范围 1-247 / Modbus slave address, range 1-247
+ ///
+ ///
+ /// 主站号 / Master Address
+ /// 通常为 0 或 1 / Usually 0 or 1
+ ///
public ModbusRtuProtocol(byte slaveAddress, byte masterAddress)
: this(ConfigurationReader.GetValueDirect("COM:Modbus", "COM"), slaveAddress, masterAddress)
{
}
///
- /// 构造函数
+ /// 构造函数 (指定串口) / Constructor (Specify COM Port)
+ ///
+ /// 使用指定的串口名称创建 Modbus RTU 协议实例
+ /// Create Modbus RTU protocol instance with specified COM port name
+ ///
+ /// 串口配置从 appsettings.json 读取
+ /// Serial port configuration is read from appsettings.json
+ ///
+ ///
///
- /// 串口
- /// 从站号
- /// 主站号
+ ///
+ /// 串口名称 / COM Port Name
+ /// 如 "COM1", "COM2" 等 / e.g., "COM1", "COM2", etc.
+ ///
+ /// 从站号 / Slave Address
+ /// 主站号 / Master Address
public ModbusRtuProtocol(string com, byte slaveAddress, byte masterAddress)
: base(slaveAddress, masterAddress)
{
+ // 创建 Modbus RTU 协议链接器 / Create Modbus RTU protocol linker
+ // 自动从配置读取串口参数 (波特率、校验位等)
+ // Automatically read serial port parameters from configuration (baud rate, parity, etc.)
ProtocolLinker = new ModbusRtuProtocolLinker(com, slaveAddress);
}
}
-}
\ No newline at end of file
+}
diff --git a/Modbus.Net/Modbus.Net.Modbus/ModbusRtuProtocolLinker.cs b/Modbus.Net/Modbus.Net.Modbus/ModbusRtuProtocolLinker.cs
index aaeb218..5446e21 100644
--- a/Modbus.Net/Modbus.Net.Modbus/ModbusRtuProtocolLinker.cs
+++ b/Modbus.Net/Modbus.Net.Modbus/ModbusRtuProtocolLinker.cs
@@ -1,35 +1,136 @@
-namespace Modbus.Net.Modbus
+namespace Modbus.Net.Modbus
{
///
- /// Modbus/Rtu协议连接器
+ /// Modbus/RTU 协议连接器 / Modbus/RTU Protocol Linker
+ ///
+ /// 实现 Modbus RTU 协议的连接器,继承自 ComProtocolLinker
+ /// Implements Modbus RTU protocol linker, inherits from ComProtocolLinker
+ ///
+ /// 主要功能 / Main Functions:
+ ///
+ /// - 串口连接管理 / Serial connection management
+ /// - CRC16 校验 / CRC16 checksum
+ /// - 响应校验 / Response validation
+ /// - 错误检测 / Error detection
+ ///
+ ///
+ ///
+ /// RTU 帧格式 / RTU Frame Format:
+ ///
+ /// [从站地址 (1)][功能码 (1)][数据 (N)][CRC 低 (1)][CRC 高 (1)]
+ /// │ │ │ │ │
+ /// └─ 设备地址 └─ 操作类型 └─ 寄存器值 └─ CRC16 校验
+ ///
+ ///
+ ///
+ /// 使用示例 / Usage Example:
+ ///
+ /// // 创建 Modbus RTU 连接器 / Create Modbus RTU linker
+ /// var linker = new ModbusRtuProtocolLinker("COM1", slaveAddress: 1);
+ ///
+ /// // 连接设备 / Connect to device
+ /// await linker.ConnectAsync();
+ ///
+ /// // 发送数据 / Send data
+ /// byte[] request = [0x01, 0x03, 0x00, 0x00, 0x00, 0x0A, 0xC4, 0x0B];
+ /// byte[] response = await linker.SendReceiveAsync(request);
+ ///
+ /// // CheckRight 会自动校验 CRC 和协议错误
+ /// // CheckRight will automatically validate CRC and protocol errors
+ ///
+ ///
+ ///
///
public class ModbusRtuProtocolLinker : ComProtocolLinker
{
///
- /// 构造函数
+ /// 构造函数 / Constructor
+ ///
+ /// 初始化 Modbus RTU 协议连接器
+ /// Initialize Modbus RTU protocol linker
+ ///
///
- /// 串口地址
- /// 从站号
+ ///
+ /// 串口地址 / Serial Port Address
+ ///
+ /// 如 "COM1", "COM2" 等
+ /// e.g., "COM1", "COM2", etc.
+ ///
+ ///
+ ///
+ /// 从站号 / Slave Address
+ ///
+ /// Modbus 从站地址,范围 1-247
+ /// Modbus slave address, range 1-247
+ ///
+ ///
public ModbusRtuProtocolLinker(string com, int slaveAddress)
: base(com, slaveAddress)
{
}
///
- /// 校验返回数据
+ /// 校验返回数据 / Validate Return Data
+ ///
+ /// 校验从设备返回的 Modbus RTU 响应数据
+ /// Validate Modbus RTU response data returned from device
+ ///
+ /// 校验流程 / Validation Flow:
+ ///
+ /// - 调用基类校验 (串口连接状态) / Call base validation (serial connection status)
+ /// - CRC16 校验 / CRC16 checksum
+ /// - 如果 CRC 失败,抛出错误 / If CRC fails, throw error
+ /// - 检查功能码 / Check function code
+ /// - 如果功能码>127,表示错误响应 / If function code>127, indicates error response
+ /// - 抛出 ModbusProtocolErrorException / Throw ModbusProtocolErrorException
+ ///
+ ///
+ ///
+ /// 错误码说明 / Error Code Description:
+ ///
+ /// - 501: CRC 校验失败 / CRC check failed
+ /// - 功能码 +128: 异常响应 / Function code +128: Exception response
+ /// - 异常码在 content[2] / Exception code in content[2]
+ ///
+ ///
+ ///
///
- /// 设备返回的数据
- /// 数据是否正确
+ ///
+ /// 设备返回的数据 / Data Returned from Device
+ ///
+ /// 包含 CRC 的完整 Modbus RTU 响应
+ /// Complete Modbus RTU response with CRC
+ ///
+ ///
+ ///
+ /// 数据是否正确 / Whether Data is Correct
+ ///
+ ///
+ /// - true: 数据正确 / Data correct
+ /// - false: 数据错误 / Data error
+ /// - null: 无法判断 / Cannot determine
+ ///
+ ///
+ ///
+ ///
+ /// 当 CRC 失败或功能码>127 时抛出 Modbus 协议错误
+ /// Throw Modbus protocol error when CRC fails or function code>127
+ ///
public override bool? CheckRight(byte[] content)
{
+ // 基类校验 (串口连接状态) / Base validation (serial connection status)
if (base.CheckRight(content) != true) return base.CheckRight(content);
- //CRC校验失败
+
+ // CRC16 校验 / CRC16 checksum
if (!Crc16.GetInstance().CrcEfficacy(content))
- throw new ModbusProtocolErrorException(501);
- //Modbus协议错误
+ throw new ModbusProtocolErrorException(501); // CRC 校验失败 / CRC check failed
+
+ // Modbus 协议错误检测 / Modbus protocol error detection
+ // 功能码>127 表示异常响应 / Function code>127 indicates exception response
if (content[1] > 127)
- throw new ModbusProtocolErrorException(content[2]);
+ throw new ModbusProtocolErrorException(content[2]); // 异常码 / Exception code
+
return true;
}
}
-}
\ No newline at end of file
+}
diff --git a/Modbus.Net/Modbus.Net.Modbus/ModbusRtuProtocolReceiver.cs b/Modbus.Net/Modbus.Net.Modbus/ModbusRtuProtocolReceiver.cs
index 5a0ac32..37680b6 100644
--- a/Modbus.Net/Modbus.Net.Modbus/ModbusRtuProtocolReceiver.cs
+++ b/Modbus.Net/Modbus.Net.Modbus/ModbusRtuProtocolReceiver.cs
@@ -1,21 +1,111 @@
-using System;
+using System;
namespace Modbus.Net.Modbus
{
+ ///
+ /// Modbus RTU 协议接收器类 / Modbus RTU Protocol Receiver Class
+ ///
+ /// 实现 Modbus RTU 从站/服务器端的数据接收和解析功能
+ /// Implements data reception and parsing functionality for Modbus RTU slave/server side
+ ///
+ /// 主要功能 / Main Functions:
+ ///
+ /// - 接收 Modbus RTU 请求帧 / Receive Modbus RTU request frames
+ /// - 解析请求数据 / Parse request data
+ /// - 提取功能码、地址、数据等信息 / Extract function code, address, data, etc.
+ /// - 支持读写操作解析 / Supports read/write operation parsing
+ ///
+ ///
+ ///
+ /// 帧格式 / Frame Format:
+ ///
+ /// - 读操作 (6 字节): [从站][功能码][地址 (2)][数量 (2)][CRC]
+ /// - 写单线圈/寄存器 (6 字节): [从站][功能码][地址 (2)][值 (2)][CRC]
+ /// - 写多线圈/寄存器 (N+7 字节): [从站][功能码][地址 (2)][数量 (2)][字节数][数据...][CRC]
+ ///
+ ///
+ ///
+ /// 使用示例 / Usage Example:
+ ///
+ /// // 创建 RTU 协议接收器 / Create RTU protocol receiver
+ /// var receiver = new ModbusRtuProtocolReceiver("COM1", slaveAddress: 1);
+ ///
+ /// // 接收器会自动解析接收到的 RTU 帧
+ /// // Receiver will automatically parse received RTU frames
+ ///
+ /// // 解析结果示例 / Parse result example:
+ /// // 接收:[0x01, 0x03, 0x00, 0x00, 0x00, 0x0A, 0xC4, 0x0B]
+ /// // 解析:
+ /// // - SlaveAddress: 0x01
+ /// // - FunctionCode: 0x03 (读保持寄存器)
+ /// // - StartAddress: 0x0000
+ /// // - Count: 0x000A (10 个寄存器)
+ ///
+ ///
+ ///
+ ///
public class ModbusRtuProtocolReceiver : ProtocolReceiver
{
+ ///
+ /// 构造函数 / Constructor
+ ///
+ /// 初始化 Modbus RTU 协议接收器
+ /// Initialize Modbus RTU protocol receiver
+ ///
+ ///
+ ///
+ /// 串口名称 / Serial Port Name
+ ///
+ /// 如 "COM1", "COM2" 等
+ /// e.g., "COM1", "COM2", etc.
+ ///
+ ///
+ ///
+ /// 从站号 / Slave Address
+ ///
+ /// Modbus 从站地址,范围 1-247
+ /// Modbus slave address, range 1-247
+ ///
+ ///
public ModbusRtuProtocolReceiver(string com, int slaveAddress)
: base(com, slaveAddress)
{
-
}
+ ///
+ /// 数据解析函数 / Data Explanation Function
+ ///
+ /// 将接收到的字节数组解析为 ReceiveDataDef 结构
+ /// Parse received byte array into ReceiveDataDef structure
+ ///
+ /// 解析规则 / Parsing Rules:
+ ///
+ /// - 长度>6: 写多线圈/寄存器操作 / Write multiple coils/registers
+ /// - 长度=6: 读操作或写单线圈/寄存器 / Read operation or write single coil/register
+ /// - 长度<6: 无效帧 / Invalid frame
+ ///
+ ///
+ ///
+ /// 返回数据结构 / Return Data Structure:
+ ///
+ /// - SlaveAddress - 从站地址 / Slave address
+ /// - FunctionCode - 功能码 / Function code
+ /// - StartAddress - 起始地址 / Start address
+ /// - Count - 数量 / Count
+ /// - WriteByteCount - 写字节数 / Write byte count
+ /// - WriteContent - 写入的数据 / Write data
+ ///
+ ///
+ ///
+ ///
protected override Func DataExplain
{
get
{
return receiveBytes =>
{
+ // 写多线圈/寄存器操作 (长度>6)
+ // Write multiple coils/registers operation (length>6)
var writeContent = receiveBytes.Length > 6 ? new byte[receiveBytes.Length - 7] : null;
if (receiveBytes.Length > 6)
{
@@ -30,8 +120,12 @@ namespace Modbus.Net.Modbus
WriteContent = writeContent
};
}
+ // 读操作或写单线圈/寄存器 (长度=6)
+ // Read operation or write single coil/register (length=6)
else if (receiveBytes.Length == 6)
{
+ // 读线圈 (01) 或读寄存器 (03)
+ // Read coils (01) or read registers (03)
if (receiveBytes[1] == 1 || receiveBytes[1] == 3)
{
writeContent = null;
@@ -45,6 +139,8 @@ namespace Modbus.Net.Modbus
WriteContent = writeContent
};
}
+ // 写单线圈 (05) 或写单寄存器 (06)
+ // Write single coil (05) or write single register (06)
else
{
writeContent = new byte[2] { receiveBytes[4], receiveBytes[5] };
@@ -59,9 +155,10 @@ namespace Modbus.Net.Modbus
};
}
}
+ // 无效帧 / Invalid frame
else return null;
};
}
}
}
-}
\ No newline at end of file
+}
diff --git a/Modbus.Net/Modbus.Net.Modbus/ModbusTcpProtocol.cs b/Modbus.Net/Modbus.Net.Modbus/ModbusTcpProtocol.cs
index a609124..ec87890 100644
--- a/Modbus.Net/Modbus.Net.Modbus/ModbusTcpProtocol.cs
+++ b/Modbus.Net/Modbus.Net.Modbus/ModbusTcpProtocol.cs
@@ -1,43 +1,132 @@
-namespace Modbus.Net.Modbus
+namespace Modbus.Net.Modbus
{
///
- /// Modbus/Tcp协议
+ /// Modbus/TCP 协议类 / Modbus/TCP Protocol Class
+ ///
+ /// 实现 Modbus TCP 协议,用于以太网通信
+ /// Implements Modbus TCP protocol for Ethernet communication
+ ///
+ /// 协议特点 / Protocol Characteristics:
+ ///
+ /// - 基于 TCP/IP 传输 / Based on TCP/IP transport
+ /// - 添加 MBAP 头 (6 字节) / Adds MBAP header (6 bytes)
+ /// - 无需 CRC 校验 (TCP 已保证) / No CRC needed (TCP guarantees)
+ /// - 默认端口:502 / Default port: 502
+ ///
+ ///
+ ///
+ /// MBAP 头格式 / MBAP Header Format:
+ ///
+ /// [Transaction ID (2 字节)][Protocol ID (2 字节)][Length (2 字节)][Unit ID (1 字节)]
+ /// │ │ │ │ │ │ │
+ /// └─ 事务标识,用于匹配请求响应
+ /// └─ 协议标识,Modbus=0
+ /// └─ 后续字节长度
+ /// └─ 从站地址
+ ///
+ ///
+ ///
+ /// 使用示例 / Usage Example:
+ ///
+ /// // 创建 Modbus TCP 协议实例 / Create Modbus TCP protocol instance
+ /// var protocol = new ModbusTcpProtocol("192.168.1.100", 502, slaveAddress: 1, masterAddress: 0);
+ ///
+ /// // 或者从配置读取 / Or read from configuration
+ /// var protocolFromConfig = new ModbusTcpProtocol(slaveAddress: 1, masterAddress: 0);
+ /// // 配置项:TCP:Modbus:IP = "192.168.1.100"
+ ///
+ /// // 连接设备 / Connect to device
+ /// await protocol.ConnectAsync();
+ ///
+ /// // 发送读取请求 / Send read request
+ /// var inputStruct = new ReadDataModbusInputStruct(1, "4X 1", 10, addressTranslator, 0);
+ /// var outputStruct = await protocol.SendReceiveAsync<ReadDataModbusOutputStruct>(
+ /// protocol[typeof(ReadDataModbusProtocol)],
+ /// inputStruct
+ /// );
+ ///
+ ///
+ ///
///
public class ModbusTcpProtocol : ModbusProtocol
{
///
- /// 构造函数
+ /// 构造函数 (从配置读取 IP) / Constructor (Read IP from Configuration)
+ ///
+ /// 从配置文件读取 IP 地址创建 Modbus TCP 协议实例
+ /// Create Modbus TCP protocol instance with IP address read from configuration file
+ ///
+ /// 配置项 / Configuration Item:
+ /// TCP:Modbus:IP = "192.168.1.100"
+ ///
+ ///
///
- /// 从站号
- /// 主站号
+ ///
+ /// 从站号 / Slave Address
+ /// Modbus 从站地址,范围 1-247 / Modbus slave address, range 1-247
+ ///
+ ///
+ /// 主站号 / Master Address
+ /// 通常为 0 或 1 / Usually 0 or 1
+ ///
public ModbusTcpProtocol(byte slaveAddress, byte masterAddress)
: this(ConfigurationReader.GetValueDirect("TCP:Modbus", "IP"), slaveAddress, masterAddress)
{
}
///
- /// 构造函数
+ /// 构造函数 (指定 IP) / Constructor (Specify IP)
+ ///
+ /// 使用指定的 IP 地址创建 Modbus TCP 协议实例
+ /// Create Modbus TCP protocol instance with specified IP address
+ ///
+ /// 使用默认端口 502
+ /// Uses default port 502
+ ///
+ ///
///
- /// ip地址
- /// 从站号
- /// 主站号
+ ///
+ /// IP 地址 / IP Address
+ /// 如 "192.168.1.100" / e.g., "192.168.1.100"
+ ///
+ /// 从站号 / Slave Address
+ /// 主站号 / Master Address
public ModbusTcpProtocol(string ip, byte slaveAddress, byte masterAddress)
: base(slaveAddress, masterAddress)
{
+ // 创建 Modbus TCP 协议链接器 / Create Modbus TCP protocol linker
ProtocolLinker = new ModbusTcpProtocolLinker(ip);
}
///
- /// 构造函数
+ /// 构造函数 (指定 IP 和端口) / Constructor (Specify IP and Port)
+ ///
+ /// 使用指定的 IP 地址和端口创建 Modbus TCP 协议实例
+ /// Create Modbus TCP protocol instance with specified IP address and port
+ ///
+ /// 适用于非标准端口的 Modbus TCP 设备
+ /// Suitable for Modbus TCP devices with non-standard ports
+ ///
+ ///
///
- /// ip地址
- /// 端口
- /// 从站号
- /// 主站号
+ ///
+ /// IP 地址 / IP Address
+ /// 如 "192.168.1.100" / e.g., "192.168.1.100"
+ ///
+ ///
+ /// 端口号 / Port Number
+ ///
+ /// 默认 502,串口服务器常用 8899
+ /// Default 502, commonly 8899 for serial servers
+ ///
+ ///
+ /// 从站号 / Slave Address
+ /// 主站号 / Master Address
public ModbusTcpProtocol(string ip, int port, byte slaveAddress, byte masterAddress)
: base(slaveAddress, masterAddress)
{
+ // 创建带端口的 Modbus TCP 协议链接器 / Create Modbus TCP protocol linker with port
ProtocolLinker = new ModbusTcpProtocolLinker(ip, port);
}
}
-}
\ No newline at end of file
+}
diff --git a/Modbus.Net/Modbus.Net.Modbus/ModbusTcpProtocolLinker.cs b/Modbus.Net/Modbus.Net.Modbus/ModbusTcpProtocolLinker.cs
index 1ba6cea..bb17397 100644
--- a/Modbus.Net/Modbus.Net.Modbus/ModbusTcpProtocolLinker.cs
+++ b/Modbus.Net/Modbus.Net.Modbus/ModbusTcpProtocolLinker.cs
@@ -1,40 +1,156 @@
-namespace Modbus.Net.Modbus
+namespace Modbus.Net.Modbus
{
///
- /// Modbus/Tcp协议连接器
+ /// Modbus/TCP 协议连接器 / Modbus/TCP Protocol Linker
+ ///
+ /// 实现 Modbus TCP 协议的连接器,继承自 TcpProtocolLinker
+ /// Implements Modbus TCP protocol linker, inherits from TcpProtocolLinker
+ ///
+ /// 主要功能 / Main Functions:
+ ///
+ /// - TCP 连接管理 / TCP connection management
+ /// - MBAP 头处理 / MBAP header handling
+ /// - 响应校验 / Response validation
+ /// - 错误检测 / Error detection
+ ///
+ ///
+ ///
+ /// MBAP 头格式 / MBAP Header Format:
+ ///
+ /// [Transaction ID (2)][Protocol ID (2)][Length (2)][Unit ID (1)]
+ /// │ │ │ │ │ │
+ /// └─ 事务标识,用于匹配请求响应
+ /// └─ 协议标识,Modbus=0
+ /// └─ 后续字节长度
+ /// └─ 从站地址
+ ///
+ ///
+ ///
+ /// 使用示例 / Usage Example:
+ ///
+ /// // 创建 Modbus TCP 连接器 / Create Modbus TCP linker
+ /// var linker = new ModbusTcpProtocolLinker("192.168.1.100", 502);
+ ///
+ /// // 连接设备 / Connect to device
+ /// await linker.ConnectAsync();
+ ///
+ /// // 发送数据 / Send data
+ /// byte[] request = [0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x00, 0x0A];
+ /// byte[] response = await linker.SendReceiveAsync(request);
+ ///
+ /// // CheckRight 会自动校验响应
+ /// // CheckRight will automatically validate response
+ ///
+ ///
+ ///
///
public class ModbusTcpProtocolLinker : TcpProtocolLinker
{
///
- /// 构造函数
+ /// 构造函数 (从配置读取端口) / Constructor (Read Port from Configuration)
+ ///
+ /// 从配置文件读取端口创建 Modbus TCP 连接器
+ /// Create Modbus TCP linker with port read from configuration file
+ ///
+ /// 配置项 / Configuration Items:
+ ///
+ /// - TCP:{IP}:ModbusPort - 指定 IP 的端口 / Port for specified IP
+ /// - TCP:Modbus:ModbusPort - 默认 Modbus 端口 / Default Modbus port
+ /// - 默认端口:502 / Default port: 502
+ ///
+ ///
+ ///
///
- /// IP地址
+ ///
+ /// IP 地址 / IP Address
+ ///
+ /// Modbus TCP 设备的 IP 地址
+ /// IP address of Modbus TCP device
+ ///
+ ///
public ModbusTcpProtocolLinker(string ip)
: this(ip, int.Parse(ConfigurationReader.GetValueDirect("TCP:" + ip, "ModbusPort") ?? ConfigurationReader.GetValueDirect("TCP:Modbus", "ModbusPort")))
{
}
///
- /// 构造函数
+ /// 构造函数 (指定 IP 和端口) / Constructor (Specify IP and Port)
+ ///
+ /// 使用指定的 IP 地址和端口创建 Modbus TCP 连接器
+ /// Create Modbus TCP linker with specified IP address and port
+ ///
///
- /// IP地址
- /// 端口
+ ///
+ /// IP 地址 / IP Address
+ /// Modbus TCP 设备的 IP 地址 / IP address of Modbus TCP device
+ ///
+ ///
+ /// 端口号 / Port Number
+ ///
+ /// Modbus TCP 默认端口:502
+ /// Modbus TCP default port: 502
+ ///
+ ///
public ModbusTcpProtocolLinker(string ip, int port) : base(ip, port)
{
}
///
- /// 校验返回数据
+ /// 校验返回数据 / Validate Return Data
+ ///
+ /// 校验从设备返回的 Modbus TCP 响应数据
+ /// Validate Modbus TCP response data returned from device
+ ///
+ /// 校验流程 / Validation Flow:
+ ///
+ /// - 调用基类校验 (TCP 连接状态) / Call base validation (TCP connection status)
+ /// - 检查 MBAP 头第 8 字节 (功能码) / Check MBAP header byte 8 (function code)
+ /// - 如果功能码>127,表示错误响应 / If function code>127, indicates error response
+ /// - 抛出 ModbusProtocolErrorException / Throw ModbusProtocolErrorException
+ ///
+ ///
+ ///
+ /// 错误码说明 / Error Code Description:
+ ///
+ /// - 功能码 +128: 异常响应 / Function code +128: Exception response
+ /// - 异常码在 content[8] / Exception code in content[8]
+ ///
+ ///
+ ///
///
- /// 设备返回的数据
- /// 数据是否正确
+ ///
+ /// 设备返回的数据 / Data Returned from Device
+ ///
+ /// 包含 MBAP 头的完整 Modbus TCP 响应
+ /// Complete Modbus TCP response with MBAP header
+ ///
+ ///
+ ///
+ /// 数据是否正确 / Whether Data is Correct
+ ///
+ ///
+ /// - true: 数据正确 / Data correct
+ /// - false: 数据错误 / Data error
+ /// - null: 无法判断 / Cannot determine
+ ///
+ ///
+ ///
+ ///
+ /// 当功能码>127 时抛出 Modbus 协议错误
+ /// Throw Modbus protocol error when function code>127
+ ///
public override bool? CheckRight(byte[] content)
{
+ // 基类校验 (TCP 连接状态) / Base validation (TCP connection status)
if (base.CheckRight(content) != true) return base.CheckRight(content);
- //Modbus协议错误
+
+ // Modbus 协议错误检测 / Modbus protocol error detection
+ // MBAP 头第 8 字节是功能码 / Byte 8 of MBAP header is function code
if (content[7] > 127)
+ // 功能码>127 表示异常响应 / Function code>127 indicates exception response
throw new ModbusProtocolErrorException(content[2] > 0 ? content[2] : content[8]);
+
return true;
}
}
-}
\ No newline at end of file
+}
diff --git a/Modbus.Net/Modbus.Net.Modbus/ModbusType.cs b/Modbus.Net/Modbus.Net.Modbus/ModbusType.cs
new file mode 100644
index 0000000..05af290
--- /dev/null
+++ b/Modbus.Net/Modbus.Net.Modbus/ModbusType.cs
@@ -0,0 +1,222 @@
+using System;
+
+namespace Modbus.Net.Modbus
+{
+ ///
+ /// Modbus 连接类型枚举 / Modbus Connection Type Enum
+ ///
+ /// 定义 Modbus 协议支持的各种连接方式
+ /// Defines various connection methods supported by Modbus protocol
+ ///
+ /// 连接类型说明 / Connection Type Description:
+ ///
+ /// - Rtu - 串行 RTU 模式 (最常用) / Serial RTU mode (most common)
+ /// - Tcp - 以太网 TCP 模式 / Ethernet TCP mode
+ /// - Ascii - 串行 ASCII 模式 / Serial ASCII mode
+ /// - RtuInTcp - TCP 透传 RTU 数据 / RTU data over TCP tunneling
+ /// - AsciiInTcp - TCP 透传 ASCII 数据 / ASCII data over TCP tunneling
+ /// - Udp - UDP 模式 / UDP mode
+ /// - RtuInUdp - UDP 透传 RTU 数据 / RTU data over UDP tunneling
+ /// - AsciiInUdp - UDP 透传 ASCII 数据 / ASCII data over UDP tunneling
+ ///
+ ///
+ ///
+ /// 使用示例 / Usage Example:
+ ///
+ /// // Modbus TCP 连接 / Modbus TCP connection
+ /// var utility = new ModbusUtility(
+ /// ModbusType.Tcp,
+ /// "192.168.1.100:502",
+ /// slaveAddress: 1,
+ /// masterAddress: 0,
+ /// endian: Endian.BigEndianLsb
+ /// );
+ ///
+ /// // Modbus RTU 串口连接 / Modbus RTU serial connection
+ /// var serialUtility = new ModbusUtility(
+ /// ModbusType.Rtu,
+ /// "COM1",
+ /// slaveAddress: 1,
+ /// masterAddress: 0,
+ /// endian: Endian.BigEndianLsb
+ /// );
+ ///
+ /// // Modbus RTU over TCP (串口服务器) / Modbus RTU over TCP (Serial Server)
+ /// var tunnelUtility = new ModbusUtility(
+ /// ModbusType.RtuInTcp,
+ /// "192.168.1.200:8899", // 串口服务器地址
+ /// slaveAddress: 1,
+ /// masterAddress: 0,
+ /// endian: Endian.BigEndianLsb
+ /// );
+ ///
+ ///
+ ///
+ ///
+ public enum ModbusType
+ {
+ ///
+ /// RTU 连接 (串行) / RTU Connection (Serial)
+ ///
+ ///
+ /// 特点 / Characteristics:
+ ///
+ /// - 二进制编码,效率高 / Binary encoding, high efficiency
+ /// - CRC16 校验 / CRC16 checksum
+ /// - 最常用的 Modbus 模式 / Most common Modbus mode
+ /// - 适用于 RS-232/RS-485 串口 / Suitable for RS-232/RS-485 serial
+ ///
+ ///
+ ///
+ /// 帧格式 / Frame Format:
+ /// [从站地址][功能码][数据][CRC 低][CRC 高]
+ ///
+ ///
+ ///
+ Rtu = 0,
+
+ ///
+ /// TCP 连接 (以太网) / TCP Connection (Ethernet)
+ ///
+ ///
+ /// 特点 / Characteristics:
+ ///
+ /// - 基于以太网 / Based on Ethernet
+ /// - 添加 MBAP 头 (6 字节) / Adds MBAP header (6 bytes)
+ /// - 无需 CRC 校验 (TCP 已保证) / No CRC needed (TCP guarantees)
+ /// - 端口:502 / Port: 502
+ ///
+ ///
+ ///
+ /// MBAP 头格式 / MBAP Header Format:
+ /// [Transaction ID (2)][Protocol ID (2)][Length (2)][Unit ID (1)]
+ ///
+ ///
+ ///
+ Tcp = 1,
+
+ ///
+ /// ASCII 连接 (串行) / ASCII Connection (Serial)
+ ///
+ ///
+ /// 特点 / Characteristics:
+ ///
+ /// - ASCII 字符编码 / ASCII character encoding
+ /// - LRC 校验 / LRC checksum
+ /// - 以冒号 (:) 开始,CRLF 结束 / Starts with colon (:), ends with CRLF
+ /// - 效率较低,但易于调试 / Lower efficiency, but easy to debug
+ ///
+ ///
+ ///
+ /// 帧格式 / Frame Format:
+ /// : [从站地址][功能码][数据][LRC][CR][LF]
+ ///
+ ///
+ ///
+ Ascii = 2,
+
+ ///
+ /// RTU 连接 TCP 透传 / RTU Connection TCP Tunneling
+ ///
+ ///
+ /// 使用场景 / Use Cases:
+ ///
+ /// - 串口服务器 / Serial device server
+ /// - 通过 TCP 传输 RTU 帧 / Transmit RTU frames over TCP
+ /// - 远程串口访问 / Remote serial access
+ ///
+ ///
+ ///
+ /// 特点 / Characteristics:
+ ///
+ /// - RTU 帧原样传输 / RTU frames transmitted as-is
+ /// - 无 MBAP 头 / No MBAP header
+ /// - 保留 CRC 校验 / CRC checksum preserved
+ ///
+ ///
+ ///
+ ///
+ RtuInTcp = 3,
+
+ ///
+ /// ASCII 连接 TCP 透传 / ASCII Connection TCP Tunneling
+ ///
+ ///
+ /// 使用场景 / Use Cases:
+ ///
+ /// - 串口服务器 / Serial device server
+ /// - 通过 TCP 传输 ASCII 帧 / Transmit ASCII frames over TCP
+ ///
+ ///
+ ///
+ /// 特点 / Characteristics:
+ ///
+ /// - ASCII 帧原样传输 / ASCII frames transmitted as-is
+ /// - 无 MBAP 头 / No MBAP header
+ /// - 保留 LRC 校验 / LRC checksum preserved
+ ///
+ ///
+ ///
+ ///
+ AsciiInTcp = 4,
+
+ ///
+ /// UDP 连接 / UDP Connection
+ ///
+ ///
+ /// 特点 / Characteristics:
+ ///
+ /// - 无连接模式 / Connectionless mode
+ /// - 添加 MBAP 头 / Adds MBAP header
+ /// - 不保证可靠性 / No reliability guarantee
+ /// - 适用于广播 / Suitable for broadcast
+ ///
+ ///
+ ///
+ ///
+ Udp = 5,
+
+ ///
+ /// RTU 连接 UDP 透传 / RTU Connection UDP Tunneling
+ ///
+ ///
+ /// 使用场景 / Use Cases:
+ ///
+ /// - UDP 广播查询 / UDP broadcast query
+ /// - 通过 UDP 传输 RTU 帧 / Transmit RTU frames over UDP
+ ///
+ ///
+ ///
+ /// 特点 / Characteristics:
+ ///
+ /// - RTU 帧原样传输 / RTU frames transmitted as-is
+ /// - 无 MBAP 头 / No MBAP header
+ /// - 保留 CRC 校验 / CRC checksum preserved
+ ///
+ ///
+ ///
+ ///
+ RtuInUdp = 6,
+
+ ///
+ /// ASCII 连接 UDP 透传 / ASCII Connection UDP Tunneling
+ ///
+ ///
+ /// 使用场景 / Use Cases:
+ ///
+ /// - 通过 UDP 传输 ASCII 帧 / Transmit ASCII frames over UDP
+ ///
+ ///
+ ///
+ /// 特点 / Characteristics:
+ ///
+ /// - ASCII 帧原样传输 / ASCII frames transmitted as-is
+ /// - 无 MBAP 头 / No MBAP header
+ /// - 保留 LRC 校验 / LRC checksum preserved
+ ///
+ ///
+ ///
+ ///
+ AsciiInUdp = 7
+ }
+}
diff --git a/Modbus.Net/Modbus.Net.Modbus/ModbusUdpProtocol.cs b/Modbus.Net/Modbus.Net.Modbus/ModbusUdpProtocol.cs
index d952863..725c6e6 100644
--- a/Modbus.Net/Modbus.Net.Modbus/ModbusUdpProtocol.cs
+++ b/Modbus.Net/Modbus.Net.Modbus/ModbusUdpProtocol.cs
@@ -1,43 +1,154 @@
-namespace Modbus.Net.Modbus
+namespace Modbus.Net.Modbus
{
///
- /// Modbus/Udp协议
+ /// Modbus/UDP 协议类 / Modbus/UDP Protocol Class
+ ///
+ /// 实现 Modbus UDP 协议,用于无连接的以太网通信
+ /// Implements Modbus UDP protocol for connectionless Ethernet communication
+ ///
+ /// 协议特点 / Protocol Characteristics:
+ ///
+ /// - 基于 UDP/IP 传输 / Based on UDP/IP transport
+ /// - 添加 MBAP 头 (6 字节) / Adds MBAP header (6 bytes)
+ /// - 无连接模式 / Connectionless mode
+ /// - 不保证可靠性 / No reliability guarantee
+ /// - 适用于广播查询 / Suitable for broadcast query
+ /// - 默认端口:502 / Default port: 502
+ ///
+ ///
+ ///
+ /// 与 Modbus TCP 的区别 / Difference from Modbus TCP:
+ ///
+ /// - UDP - 无连接,不保证送达,支持广播 / Connectionless, no delivery guarantee, supports broadcast
+ /// - TCP - 面向连接,保证送达,不支持广播 / Connection-oriented, delivery guaranteed, no broadcast
+ ///
+ ///
+ ///
+ /// MBAP 头格式 / MBAP Header Format:
+ ///
+ /// [Transaction ID (2)][Protocol ID (2)][Length (2)][Unit ID (1)]
+ /// │ │ │ │ │ │
+ /// └─ 事务标识,用于匹配请求响应
+ /// └─ 协议标识,Modbus=0
+ /// └─ 后续字节长度
+ /// └─ 从站地址
+ ///
+ ///
+ ///
+ /// 使用示例 / Usage Example:
+ ///
+ /// // 创建 Modbus UDP 协议实例 / Create Modbus UDP protocol instance
+ /// var protocol = new ModbusUdpProtocol("192.168.1.100", 502, slaveAddress: 1, masterAddress: 0);
+ ///
+ /// // 或者从配置读取 / Or read from configuration
+ /// var protocolFromConfig = new ModbusUdpProtocol(slaveAddress: 1, masterAddress: 0);
+ /// // 配置项:UDP:Modbus:IP = "192.168.1.100"
+ ///
+ /// // 连接设备 (UDP 无需真正连接) / Connect to device (UDP doesn't need real connection)
+ /// await protocol.ConnectAsync();
+ ///
+ /// // 发送读取请求 / Send read request
+ /// var inputStruct = new ReadDataModbusInputStruct(1, "4X 1", 10, addressTranslator, 0);
+ /// var outputStruct = await protocol.SendReceiveAsync<ReadDataModbusOutputStruct>(
+ /// protocol[typeof(ReadDataModbusProtocol)],
+ /// inputStruct
+ /// );
+ ///
+ /// // UDP 广播查询示例 / UDP broadcast query example
+ /// var broadcastProtocol = new ModbusUdpProtocol("255.255.255.255", 502, slaveAddress: 0, masterAddress: 0);
+ /// // 注意:广播地址为 0,所有从站都会响应
+ /// // Note: Broadcast address is 0, all slaves will respond
+ ///
+ ///
+ ///
///
public class ModbusUdpProtocol : ModbusProtocol
{
///
- /// 构造函数
+ /// 构造函数 (从配置读取 IP) / Constructor (Read IP from Configuration)
+ ///
+ /// 从配置文件读取 IP 地址创建 Modbus UDP 协议实例
+ /// Create Modbus UDP protocol instance with IP address read from configuration file
+ ///
+ /// 配置项 / Configuration Item:
+ /// UDP:Modbus:IP = "192.168.1.100"
+ ///
+ ///
///
- /// 从站号
- /// 主站号
+ ///
+ /// 从站号 / Slave Address
+ /// Modbus 从站地址,范围 1-247 / Modbus slave address, range 1-247
+ ///
+ ///
+ /// 主站号 / Master Address
+ /// 通常为 0 或 1 / Usually 0 or 1
+ ///
public ModbusUdpProtocol(byte slaveAddress, byte masterAddress)
: this(ConfigurationReader.GetValueDirect("UDP:Modbus", "IP"), slaveAddress, masterAddress)
{
}
///
- /// 构造函数
+ /// 构造函数 (指定 IP) / Constructor (Specify IP)
+ ///
+ /// 使用指定的 IP 地址创建 Modbus UDP 协议实例
+ /// Create Modbus UDP protocol instance with specified IP address
+ ///
+ /// 使用默认端口 502
+ /// Uses default port 502
+ ///
+ ///
///
- /// ip地址
- /// 从站号
- /// 主站号
+ ///
+ /// IP 地址 / IP Address
+ ///
+ /// 如 "192.168.1.100" 或广播地址 "255.255.255.255"
+ /// e.g., "192.168.1.100" or broadcast address "255.255.255.255"
+ ///
+ ///
+ /// 从站号 / Slave Address
+ /// 主站号 / Master Address
public ModbusUdpProtocol(string ip, byte slaveAddress, byte masterAddress)
: base(slaveAddress, masterAddress)
{
+ // 创建 Modbus UDP 协议链接器
+ // Create Modbus UDP protocol linker
ProtocolLinker = new ModbusUdpProtocolLinker(ip);
}
///
- /// 构造函数
+ /// 构造函数 (指定 IP 和端口) / Constructor (Specify IP and Port)
+ ///
+ /// 使用指定的 IP 地址和端口创建 Modbus UDP 协议实例
+ /// Create Modbus UDP protocol instance with specified IP address and port
+ ///
+ /// 适用于非标准端口的设备
+ /// Suitable for devices with non-standard ports
+ ///
+ ///
///
- /// ip地址
- /// 端口
- /// 从站号
- /// 主站号
+ ///
+ /// IP 地址 / IP Address
+ ///
+ /// 如 "192.168.1.100" 或广播地址 "255.255.255.255"
+ /// e.g., "192.168.1.100" or broadcast address "255.255.255.255"
+ ///
+ ///
+ ///
+ /// 端口号 / Port Number
+ ///
+ /// 默认 502
+ /// Default 502
+ ///
+ ///
+ /// 从站号 / Slave Address
+ /// 主站号 / Master Address
public ModbusUdpProtocol(string ip, int port, byte slaveAddress, byte masterAddress)
: base(slaveAddress, masterAddress)
{
+ // 创建带端口的 Modbus UDP 协议链接器
+ // Create Modbus UDP protocol linker with port
ProtocolLinker = new ModbusUdpProtocolLinker(ip, port);
}
}
-}
\ No newline at end of file
+}
diff --git a/Modbus.Net/Modbus.Net.Modbus/ModbusUdpProtocolLinker.cs b/Modbus.Net/Modbus.Net.Modbus/ModbusUdpProtocolLinker.cs
index 34f19ac..b29d614 100644
--- a/Modbus.Net/Modbus.Net.Modbus/ModbusUdpProtocolLinker.cs
+++ b/Modbus.Net/Modbus.Net.Modbus/ModbusUdpProtocolLinker.cs
@@ -1,40 +1,155 @@
-namespace Modbus.Net.Modbus
+namespace Modbus.Net.Modbus
{
///
- /// Modbus/Udp协议连接器
+ /// Modbus/UDP 协议连接器 / Modbus/UDP Protocol Linker
+ ///
+ /// 实现 Modbus UDP 协议的连接器,继承自 UdpProtocolLinker
+ /// Implements Modbus UDP protocol linker, inherits from UdpProtocolLinker
+ ///
+ /// 主要功能 / Main Functions:
+ ///
+ /// - UDP 连接管理 / UDP connection management
+ /// - MBAP 头处理 / MBAP header handling
+ /// - 响应校验 / Response validation
+ /// - 错误检测 / Error detection
+ /// - 支持广播查询 / Supports broadcast query
+ ///
+ ///
+ ///
+ /// 与 TCP 的区别 / Difference from TCP:
+ ///
+ /// - 无连接模式 / Connectionless mode
+ /// - 不保证可靠性 / No reliability guarantee
+ /// - 支持广播 / Supports broadcast
+ /// - 适用于简单查询场景 / Suitable for simple query scenarios
+ ///
+ ///
+ ///
+ /// 使用示例 / Usage Example:
+ ///
+ /// // 创建 Modbus UDP 连接器 / Create Modbus UDP linker
+ /// var linker = new ModbusUdpProtocolLinker("192.168.1.100", 502);
+ ///
+ /// // 连接设备 (UDP 无需真正连接) / Connect to device (UDP doesn't need real connection)
+ /// await linker.ConnectAsync();
+ ///
+ /// // 发送数据 / Send data
+ /// byte[] request = [0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x00, 0x0A];
+ /// byte[] response = await linker.SendReceiveAsync(request);
+ ///
+ /// // CheckRight 会自动校验响应
+ /// // CheckRight will automatically validate response
+ ///
+ ///
+ ///
///
public class ModbusUdpProtocolLinker : UdpProtocolLinker
{
///
- /// 构造函数
+ /// 构造函数 (从配置读取端口) / Constructor (Read Port from Configuration)
+ ///
+ /// 从配置文件读取端口创建 Modbus UDP 连接器
+ /// Create Modbus UDP linker with port read from configuration file
+ ///
+ /// 配置项 / Configuration Items:
+ ///
+ /// - UDP:{IP}:ModbusPort - 指定 IP 的端口 / Port for specified IP
+ /// - UDP:Modbus:ModbusPort - 默认 Modbus 端口 / Default Modbus port
+ /// - 默认端口:502 / Default port: 502
+ ///
+ ///
+ ///
///
- /// IP地址
+ ///
+ /// IP 地址 / IP Address
+ ///
+ /// Modbus UDP 设备的 IP 地址
+ /// IP address of Modbus UDP device
+ ///
+ ///
public ModbusUdpProtocolLinker(string ip)
: this(ip, int.Parse(ConfigurationReader.GetValueDirect("UDP:" + ip, "ModbusPort") ?? ConfigurationReader.GetValueDirect("UDP:Modbus", "ModbusPort")))
{
}
///
- /// 构造函数
+ /// 构造函数 (指定 IP 和端口) / Constructor (Specify IP and Port)
+ ///
+ /// 使用指定的 IP 地址和端口创建 Modbus UDP 连接器
+ /// Create Modbus UDP linker with specified IP address and port
+ ///
///
- /// IP地址
- /// 端口
+ ///
+ /// IP 地址 / IP Address
+ /// Modbus UDP 设备的 IP 地址 / IP address of Modbus UDP device
+ ///
+ ///
+ /// 端口号 / Port Number
+ ///
+ /// Modbus UDP 默认端口:502
+ /// Modbus UDP default port: 502
+ ///
+ ///
public ModbusUdpProtocolLinker(string ip, int port) : base(ip, port)
{
}
///
- /// 校验返回数据
+ /// 校验返回数据 / Validate Return Data
+ ///
+ /// 校验从设备返回的 Modbus UDP 响应数据
+ /// Validate Modbus UDP response data returned from device
+ ///
+ /// 校验流程 / Validation Flow:
+ ///
+ /// - 调用基类校验 (UDP 连接状态) / Call base validation (UDP connection status)
+ /// - 检查 MBAP 头第 8 字节 (功能码) / Check MBAP header byte 8 (function code)
+ /// - 如果功能码>127,表示错误响应 / If function code>127, indicates error response
+ /// - 抛出 ModbusProtocolErrorException / Throw ModbusProtocolErrorException
+ ///
+ ///
+ ///
+ /// 错误码说明 / Error Code Description:
+ ///
+ /// - 功能码 +128: 异常响应 / Function code +128: Exception response
+ /// - 异常码在 content[8] / Exception code in content[8]
+ ///
+ ///
+ ///
///
- /// 设备返回的数据
- /// 数据是否正确
+ ///
+ /// 设备返回的数据 / Data Returned from Device
+ ///
+ /// 包含 MBAP 头的完整 Modbus UDP 响应
+ /// Complete Modbus UDP response with MBAP header
+ ///
+ ///
+ ///
+ /// 数据是否正确 / Whether Data is Correct
+ ///
+ ///
+ /// - true: 数据正确 / Data correct
+ /// - false: 数据错误 / Data error
+ /// - null: 无法判断 / Cannot determine
+ ///
+ ///
+ ///
+ ///
+ /// 当功能码>127 时抛出 Modbus 协议错误
+ /// Throw Modbus protocol error when function code>127
+ ///
public override bool? CheckRight(byte[] content)
{
+ // 基类校验 (UDP 连接状态) / Base validation (UDP connection status)
if (base.CheckRight(content) != true) return base.CheckRight(content);
- //Modbus协议错误
+
+ // Modbus 协议错误检测 / Modbus protocol error detection
+ // MBAP 头第 8 字节是功能码 / Byte 8 of MBAP header is function code
if (content[7] > 127)
+ // 功能码>127 表示异常响应 / Function code>127 indicates exception response
throw new ModbusProtocolErrorException(content[2] > 0 ? content[2] : content[8]);
+
return true;
}
}
-}
\ No newline at end of file
+}
diff --git a/Modbus.Net/Modbus.Net.Modbus/ModbusUtility.cs b/Modbus.Net/Modbus.Net.Modbus/ModbusUtility.cs
index e2f02d2..33895c0 100644
--- a/Modbus.Net/Modbus.Net.Modbus/ModbusUtility.cs
+++ b/Modbus.Net/Modbus.Net.Modbus/ModbusUtility.cs
@@ -1,57 +1,73 @@
-using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Logging;
using System;
using System.Threading.Tasks;
namespace Modbus.Net.Modbus
{
///
- /// Modbus连接类型
- ///
- public enum ModbusType
- {
- ///
- /// Rtu连接
- ///
- Rtu = 0,
-
- ///
- /// Tcp连接
- ///
- Tcp = 1,
-
- ///
- /// Ascii连接
- ///
- Ascii = 2,
-
- ///
- /// Rtu连接Tcp透传
- ///
- RtuInTcp = 3,
-
- ///
- /// Ascii连接Tcp透传
- ///
- AsciiInTcp = 4,
-
- ///
- /// Udp连接
- ///
- Udp = 5,
-
- ///
- /// Rtu连接Udp透传
- ///
- RtuInUdp = 6,
-
- ///
- /// Ascii连接Udp透传
- ///
- AsciiInUdp = 7
- }
-
- ///
- /// Modbus基础Api入口
+ /// Modbus 基础 API 入口类 / Modbus Base API Entry Class
+ ///
+ /// 提供 Modbus 协议的完整实现,支持多种连接方式和高级功能
+ /// Provides complete Modbus protocol implementation, supporting multiple connection methods and advanced features
+ ///
+ /// 支持的连接类型 / Supported Connection Types:
+ ///
+ /// - ModbusType.Rtu - 串行 RTU 模式 (最常用) / Serial RTU mode (most common)
+ /// - ModbusType.Tcp - 以太网 TCP 模式 / Ethernet TCP mode
+ /// - ModbusType.Ascii - 串行 ASCII 模式 / Serial ASCII mode
+ /// - ModbusType.RtuInTcp - TCP 透传 RTU 数据 / RTU over TCP tunneling
+ /// - ModbusType.AsciiInTcp - TCP 透传 ASCII 数据 / ASCII over TCP tunneling
+ /// - ModbusType.Udp - UDP 模式 / UDP mode
+ /// - ModbusType.RtuInUdp - UDP 透传 RTU 数据 / RTU over UDP tunneling
+ /// - ModbusType.AsciiInUdp - UDP 透传 ASCII 数据 / ASCII over UDP tunneling
+ ///
+ ///
+ ///
+ /// 实现的功能码 / Implemented Function Codes:
+ ///
+ /// - 01 - 读线圈状态 / Read Coil Status
+ /// - 02 - 读离散输入 / Read Discrete Inputs
+ /// - 03 - 读保持寄存器 / Read Holding Registers
+ /// - 04 - 读输入寄存器 / Read Input Registers
+ /// - 05 - 写单个线圈 / Write Single Coil
+ /// - 06 - 写单个寄存器 / Write Single Register
+ /// - 15 - 写多个线圈 / Write Multiple Coils
+ /// - 16 - 写多个寄存器 / Write Multiple Registers
+ /// - 23 - 读写多个寄存器 / Read/Write Multiple Registers
+ /// - 07-08,11-12,17,20-22,24 - 其他高级功能 / Other advanced features
+ ///
+ ///
+ ///
+ /// 使用示例 / Usage Example:
+ ///
+ /// // Modbus TCP 连接 / Modbus TCP connection
+ /// var utility = new ModbusUtility(
+ /// ModbusType.Tcp,
+ /// "192.168.1.100:502",
+ /// slaveAddress: 1,
+ /// masterAddress: 0,
+ /// endian: Endian.BigEndianLsb
+ /// );
+ ///
+ /// // 连接设备 / Connect to device
+ /// await utility.ConnectAsync();
+ ///
+ /// // 读取保持寄存器 / Read holding registers
+ /// var result = await utility.GetDatasAsync<ushort>("4X 1", 10);
+ /// if (result.IsSuccess)
+ /// {
+ /// ushort[] values = result.Datas;
+ /// Console.WriteLine($"Temperature: {values[0] * 0.1}°C");
+ /// }
+ ///
+ /// // 写入寄存器 / Write registers
+ /// await utility.SetDatasAsync("4X 1", new object[] { (ushort)250, (ushort)300 });
+ ///
+ /// // 断开连接 / Disconnect
+ /// utility.Disconnect();
+ ///
+ ///
+ ///
///
public class ModbusUtility : BaseUtility, PipeUnit>,
IUtilityMethodExceptionStatus,
@@ -67,17 +83,46 @@ namespace Modbus.Net.Modbus
private static readonly ILogger logger = LogProvider.CreateLogger();
///
- /// Modbus协议类型
+ /// Modbus 协议类型 / Modbus Protocol Type
+ ///
+ /// 当前使用的 Modbus 连接类型
+ /// Current Modbus connection type in use
+ ///
///
private ModbusType _modbusType;
///
- /// 构造函数
+ /// 构造函数 (无连接字符串) / Constructor (without Connection String)
+ ///
+ /// 初始化 Modbus Utility 实例,稍后通过 SetConnectionType 设置连接
+ /// Initialize Modbus Utility instance, set connection later via SetConnectionType
+ ///
///
- /// 协议类型
- /// 从站号
- /// 主站号
- /// 端格式
+ ///
+ /// 协议类型 / Protocol Type
+ /// ModbusType 枚举值 / ModbusType enum value
+ ///
+ ///
+ /// 从站号 / Slave Address
+ ///
+ /// Modbus 从站地址,范围 1-247
+ /// Modbus slave address, range 1-247
+ ///
+ ///
+ ///
+ /// 主站号 / Master Address
+ ///
+ /// 通常为 0 或 1
+ /// Usually 0 or 1
+ ///
+ ///
+ ///
+ /// 端格式 / Endianness
+ ///
+ /// Modbus 标准使用 BigEndianLsb
+ /// Modbus standard uses BigEndianLsb
+ ///
+ ///
public ModbusUtility(int connectionType, byte slaveAddress, byte masterAddress,
Endian endian)
: base(slaveAddress, masterAddress)
@@ -89,13 +134,29 @@ namespace Modbus.Net.Modbus
}
///
- /// 构造函数
+ /// 构造函数 (带连接字符串) / Constructor (with Connection String)
+ ///
+ /// 初始化 Modbus Utility 实例并立即设置连接
+ /// Initialize Modbus Utility instance and set connection immediately
+ ///
///
- /// 协议类型
- /// 连接地址
- /// 从站号
- /// 主站号
- /// 端格式
+ ///
+ /// 协议类型 / Protocol Type
+ /// ModbusType 枚举值 / ModbusType enum value
+ ///
+ ///
+ /// 连接地址 / Connection Address
+ ///
+ /// 格式示例 / Format Examples:
+ ///
+ /// - TCP: "192.168.1.100:502"
+ /// - 串口:"COM1" 或 "COM1,9600,None,8,1"
+ ///
+ ///
+ ///
+ /// 从站号 / Slave Address
+ /// 主站号 / Master Address
+ /// 端格式 / Endianness
public ModbusUtility(ModbusType connectionType, string connectionString, byte slaveAddress, byte masterAddress,
Endian endian)
: base(slaveAddress, masterAddress)
@@ -107,12 +168,20 @@ namespace Modbus.Net.Modbus
}
///
- /// 端格式
+ /// 端格式 / Endianness
+ ///
+ /// Modbus 标准使用大端格式 (BigEndianLsb)
+ /// Modbus standard uses Big Endian format (BigEndianLsb)
+ ///
///
public override Endian Endian { get; }
///
- /// Ip地址
+ /// IP 地址 (从连接字符串提取) / IP Address (Extracted from Connection String)
+ ///
+ /// 解析连接字符串中的 IP 部分
+ /// Parse IP part from connection string
+ ///
///
protected string ConnectionStringIp
{
@@ -124,7 +193,11 @@ namespace Modbus.Net.Modbus
}
///
- /// 端口
+ /// 端口号 (从连接字符串提取) / Port Number (Extracted from Connection String)
+ ///
+ /// 解析连接字符串中的端口部分
+ /// Parse port part from connection string
+ ///
///
protected int? ConnectionStringPort
{
@@ -146,7 +219,11 @@ namespace Modbus.Net.Modbus
}
///
- /// 协议类型
+ /// 协议类型 / Protocol Type
+ ///
+ /// 设置协议类型时会自动创建相应的协议实例
+ /// Automatically creates corresponding protocol instance when setting protocol type
+ ///
///
public ModbusType ModbusType
{
@@ -154,9 +231,11 @@ namespace Modbus.Net.Modbus
set
{
_modbusType = value;
+ // 根据协议类型创建相应的协议实例
+ // Create corresponding protocol instance based on protocol type
switch (_modbusType)
{
- //Rtu协议
+ // RTU 协议 / RTU Protocol
case ModbusType.Rtu:
{
Wrapper = ConnectionString == null
@@ -164,7 +243,7 @@ namespace Modbus.Net.Modbus
: new ModbusRtuProtocol(ConnectionString, SlaveAddress, MasterAddress);
break;
}
- //Tcp协议
+ // TCP 协议 / TCP Protocol
case ModbusType.Tcp:
{
Wrapper = ConnectionString == null
@@ -175,7 +254,7 @@ namespace Modbus.Net.Modbus
MasterAddress));
break;
}
- //Ascii协议
+ // ASCII 协议 / ASCII Protocol
case ModbusType.Ascii:
{
Wrapper = ConnectionString == null
@@ -183,7 +262,7 @@ namespace Modbus.Net.Modbus
: new ModbusAsciiProtocol(ConnectionString, SlaveAddress, MasterAddress);
break;
}
- //Rtu协议Tcp透传
+ // RTU over TCP 透传 / RTU over TCP Tunneling
case ModbusType.RtuInTcp:
{
Wrapper = ConnectionString == null
@@ -194,7 +273,7 @@ namespace Modbus.Net.Modbus
MasterAddress));
break;
}
- //Ascii协议Tcp透传
+ // ASCII over TCP 透传 / ASCII over TCP Tunneling
case ModbusType.AsciiInTcp:
{
Wrapper = ConnectionString == null
@@ -205,7 +284,7 @@ namespace Modbus.Net.Modbus
MasterAddress));
break;
}
- //Tcp协议Udp透传
+ // UDP 协议 / UDP Protocol
case ModbusType.Udp:
{
Wrapper = ConnectionString == null
@@ -216,7 +295,7 @@ namespace Modbus.Net.Modbus
MasterAddress));
break;
}
- //Rtu协议Udp透传
+ // RTU over UDP 透传 / RTU over UDP Tunneling
case ModbusType.RtuInUdp:
{
Wrapper = ConnectionString == null
@@ -227,7 +306,7 @@ namespace Modbus.Net.Modbus
MasterAddress));
break;
}
- //Rtu协议Udp透传
+ // ASCII over UDP 透传 / ASCII over UDP Tunneling
case ModbusType.AsciiInUdp:
{
Wrapper = ConnectionString == null
@@ -243,24 +322,69 @@ namespace Modbus.Net.Modbus
}
///
- /// 设置协议类型
+ /// 设置协议类型 / Set Protocol Type
+ ///
+ /// 实现 BaseUtility 的抽象方法
+ /// Implements abstract method from BaseUtility
+ ///
///
- /// 协议类型
+ /// 协议类型 / Protocol Type
public override void SetConnectionType(int connectionType)
{
ModbusType = (ModbusType)connectionType;
}
- ///
+ ///
+ /// 读取数据 (基础方法) / Read Data (Base Method)
+ ///
+ /// 实现 BaseUtility 的抽象方法,读取原始字节数据
+ /// Implements abstract method from BaseUtility, reads raw byte data
+ ///
+ /// 处理流程 / Processing Flow:
+ ///
+ /// - 创建 ReadDataModbusInputStruct / Create ReadDataModbusInputStruct
+ /// - 调用协议层发送接收 / Call protocol layer send/receive
+ /// - 返回 ReadDataModbusOutputStruct 中的数据 / Return data from ReadDataModbusOutputStruct
+ /// - 处理 ModbusProtocolErrorException / Handle ModbusProtocolErrorException
+ ///
+ ///
+ ///
+ ///
+ ///
+ /// 开始地址 / Start Address
+ ///
+ /// 格式:"4X 1", "0X 10" 等
+ /// Format: "4X 1", "0X 10", etc.
+ ///
+ ///
+ /// 获取字节数个数 / Number of Bytes to Get
+ /// 获取原始个数 (用于位操作) / Get Original Count (for bit operations)
+ ///
+ /// 接收到的 byte 数据 / Received Byte Data
+ ///
+ /// ReturnStruct<byte[]> 包含:
+ /// ReturnStruct<byte[]> contains:
+ ///
+ /// - Datas: 读取的字节数组 / Read byte array
+ /// - IsSuccess: 读取是否成功 / Read success flag
+ /// - ErrorCode: 错误码 / Error code
+ /// - ErrorMsg: 错误消息 / Error message
+ ///
+ ///
+ ///
public override async Task> GetDatasAsync(string startAddress, int getByteCount, int getOriginalCount)
{
try
{
+ // 创建读取输入结构 / Create read input structure
var inputStruct = new ReadDataModbusInputStruct(SlaveAddress, startAddress,
(ushort)getByteCount, AddressTranslator, (ushort)getOriginalCount);
+
+ // 发送接收 / Send and receive
var outputStruct = await
Wrapper.SendReceiveAsync(Wrapper[typeof(ReadDataModbusProtocol)],
inputStruct);
+
return new ReturnStruct
{
Datas = outputStruct?.DataValue,
@@ -282,16 +406,62 @@ namespace Modbus.Net.Modbus
}
}
- ///
+ ///
+ /// 写入数据 (基础方法) / Write Data (Base Method)
+ ///
+ /// 实现 BaseUtility 的抽象方法,写入对象数组
+ /// Implements abstract method from BaseUtility, writes object array
+ ///
+ /// 处理流程 / Processing Flow:
+ ///
+ /// - 创建 WriteDataModbusInputStruct / Create WriteDataModbusInputStruct
+ /// - 调用协议层发送接收 / Call protocol layer send/receive
+ /// - 验证写入长度 / Verify write length
+ /// - 处理 ModbusProtocolErrorException / Handle ModbusProtocolErrorException
+ ///
+ ///
+ ///
+ ///
+ ///
+ /// 开始地址 / Start Address
+ /// 格式:"4X 1", "0X 10" 等 / Format: "4X 1", "0X 10", etc.
+ ///
+ ///
+ /// 设置数据 / Set Data
+ ///
+ /// 对象数组,如 [(ushort)100, (ushort)200]
+ /// Object array, e.g., [(ushort)100, (ushort)200]
+ ///
+ ///
+ ///
+ /// 设置原始长度 (用于位操作) / Set Original Length (for bit operations)
+ ///
+ ///
+ /// 是否设置成功 / Whether Set is Successful
+ ///
+ /// ReturnStruct<bool>:
+ ///
+ /// - Datas: true=成功,false=失败 / true=success, false=failure
+ /// - IsSuccess: 操作是否成功 / Operation success flag
+ /// - ErrorCode: 错误码 / Error code
+ /// - ErrorMsg: 错误消息 / Error message
+ ///
+ ///
+ ///
public override async Task> SetDatasAsync(string startAddress, object[] setContents, int setOriginalCount)
{
try
{
+ // 创建写入输入结构 / Create write input structure
var inputStruct = new WriteDataModbusInputStruct(SlaveAddress, startAddress, setContents,
AddressTranslator, Endian, (ushort)setOriginalCount);
+
+ // 发送接收 / Send and receive
var outputStruct = await
Wrapper.SendReceiveAsync(Wrapper[typeof(WriteDataModbusProtocol)],
inputStruct);
+
+ // 验证写入长度 / Verify write length
var ans = outputStruct?.WriteCount * 2 == BigEndianLsbValueHelper.Instance.ObjectArrayToByteArray(setContents).Length;
return new ReturnStruct()
{
@@ -314,7 +484,14 @@ namespace Modbus.Net.Modbus
}
}
- ///
+ ///
+ /// 获取异常状态 (功能码 07) / Get Exception Status (Function Code 07)
+ ///
+ /// 仅用于串行通信 (RTU/ASCII)
+ /// For serial communication only (RTU/ASCII)
+ ///
+ ///
+ /// 异常状态字节 / Exception Status Byte
public async Task> GetExceptionStatusAsync()
{
try
@@ -344,7 +521,16 @@ namespace Modbus.Net.Modbus
}
}
- ///
+ ///
+ /// 诊断功能 (功能码 08) / Diagnostics (Function Code 08)
+ ///
+ /// 仅用于串行通信 (RTU/ASCII)
+ /// For serial communication only (RTU/ASCII)
+ ///
+ ///
+ /// 子功能码 / Sub-function Code
+ /// 数据数组 / Data Array
+ /// 诊断数据 / Diagnostics Data
public async Task> GetDiagnoticsAsync(ushort subFunction, ushort[] data)
{
try
@@ -374,7 +560,14 @@ namespace Modbus.Net.Modbus
}
}
- ///
+ ///
+ /// 获取通讯事件计数器 (功能码 11) / Get Comm Event Counter (Function Code 11)
+ ///
+ /// 仅用于串行通信 (RTU/ASCII)
+ /// For serial communication only (RTU/ASCII)
+ ///
+ ///
+ /// 通讯事件计数器数据 / Comm Event Counter Data
public async Task> GetCommEventCounterAsync()
{
try
@@ -404,7 +597,14 @@ namespace Modbus.Net.Modbus
}
}
- ///
+ ///
+ /// 获取通讯事件日志 (功能码 12) / Get Comm Event Log (Function Code 12)
+ ///
+ /// 仅用于串行通信 (RTU/ASCII)
+ /// For serial communication only (RTU/ASCII)
+ ///
+ ///
+ /// 通讯事件日志数据 / Comm Event Log Data
public async Task> GetCommEventLogAsync()
{
try
@@ -434,7 +634,14 @@ namespace Modbus.Net.Modbus
}
}
- ///
+ ///
+ /// 报告从站 ID (功能码 17) / Report Slave ID (Function Code 17)
+ ///
+ /// 仅用于串行通信 (RTU/ASCII)
+ /// For serial communication only (RTU/ASCII)
+ ///
+ ///
+ /// 从站 ID 数据 / Slave ID Data
public async Task> GetSlaveIdAsync()
{
try
@@ -464,7 +671,15 @@ namespace Modbus.Net.Modbus
}
}
- ///
+ ///
+ /// 读文件记录 (功能码 20) / Read File Record (Function Code 20)
+ ///
+ /// 读取从站文件记录
+ /// Read slave file records
+ ///
+ ///
+ /// 文件记录定义数组 / File Record Definition Array
+ /// 文件记录输出定义数组 / File Record Output Definition Array
public async Task> GetFileRecordAsync(ReadFileRecordInputDef[] recordDefs)
{
try
@@ -494,7 +709,15 @@ namespace Modbus.Net.Modbus
}
}
- ///
+ ///
+ /// 写文件记录 (功能码 21) / Write File Record (Function Code 21)
+ ///
+ /// 写入从站文件记录
+ /// Write slave file records
+ ///
+ ///
+ /// 文件记录输入定义数组 / File Record Input Definition Array
+ /// 文件记录输出定义数组 / File Record Output Definition Array
public async Task> SetFileRecordAsync(WriteFileRecordInputDef[] recordDefs)
{
try
@@ -524,7 +747,17 @@ namespace Modbus.Net.Modbus
}
}
- ///
+ ///
+ /// 写寄存器掩码 (功能码 22) / Mask Write Register (Function Code 22)
+ ///
+ /// 对寄存器进行 AND/OR 掩码操作
+ /// Perform AND/OR mask operation on register
+ ///
+ ///
+ /// 参考地址 / Reference Address
+ /// AND 掩码 / AND Mask
+ /// OR 掩码 / OR Mask
+ /// 掩码寄存器数据 / Mask Register Data
public async Task> SetMaskRegister(ushort referenceAddress, ushort andMask, ushort orMask)
{
try
@@ -554,7 +787,18 @@ namespace Modbus.Net.Modbus
}
}
- ///
+ ///
+ /// 读写多个寄存器 (功能码 23) / Read/Write Multiple Registers (Function Code 23)
+ ///
+ /// 原子操作:先读后写
+ /// Atomic operation: read then write
+ ///
+ ///
+ /// 读起始地址 / Read Starting Address
+ /// 读数量 / Quantity to Read
+ /// 写起始地址 / Write Starting Address
+ /// 写值数组 / Write Values Array
+ /// 读取的寄存器值数组 / Read Register Values Array
public async Task> GetMultipleRegister(ushort readStartingAddress, ushort quantityToRead, ushort writeStartingAddress, ushort[] writeValues)
{
try
@@ -584,7 +828,15 @@ namespace Modbus.Net.Modbus
}
}
- ///
+ ///
+ /// 读 FIFO 队列 (功能码 24) / Read FIFO Queue (Function Code 24)
+ ///
+ /// 读取从站 FIFO 队列
+ /// Read slave FIFO queue
+ ///
+ ///
+ /// FIFO 指针地址 / FIFO Pointer Address
+ /// FIFO 值寄存器数组 / FIFO Value Register Array
public async Task> GetFIFOQueue(ushort fifoPointerAddress)
{
try
@@ -614,4 +866,4 @@ namespace Modbus.Net.Modbus
}
}
}
-}
\ No newline at end of file
+}
diff --git a/Modbus.Net/Modbus.Net.Modbus/ModbusUtilityServer.cs b/Modbus.Net/Modbus.Net.Modbus/ModbusUtilityServer.cs
index 5c4c258..46935ee 100644
--- a/Modbus.Net/Modbus.Net.Modbus/ModbusUtilityServer.cs
+++ b/Modbus.Net/Modbus.Net.Modbus/ModbusUtilityServer.cs
@@ -1,7 +1,56 @@
-namespace Modbus.Net.Modbus
+namespace Modbus.Net.Modbus
{
+ ///
+ /// Modbus 服务器端工具类 / Modbus Server-Side Utility Class
+ ///
+ /// 实现 Modbus 从站/服务器端功能
+ /// Implements Modbus slave/server-side functionality
+ ///
+ /// 主要功能 / Main Functions:
+ ///
+ /// - 响应客户端读取请求 / Respond to client read requests
+ /// - 响应客户端写入请求 / Respond to client write requests
+ /// - 维护内部数据寄存器 / Maintain internal data registers
+ /// - 处理 Modbus RTU/TCP 协议 / Handle Modbus RTU/TCP protocols
+ ///
+ ///
+ ///
+ /// 使用场景 / Use Cases:
+ ///
+ /// - Modbus 从站设备模拟 / Modbus slave device simulation
+ /// - 虚拟 PLC / Virtual PLC
+ /// - 设备仿真测试 / Device simulation testing
+ /// - 协议网关 / Protocol gateway
+ ///
+ ///
+ ///
+ /// 实现说明 / Implementation Notes:
+ ///
+ /// - 继承自 BaseUtilityServer / Inherits from BaseUtilityServer
+ /// - 需要实现 GetServerDatasAsync 和 SetServerDatasAsync / Need to implement GetServerDatasAsync and SetServerDatasAsync
+ /// - 维护线圈 (0X) 和寄存器 (4X) 数据 / Maintain coil (0X) and register (4X) data
+ ///
+ ///
+ ///
+ /// TODO: 待实现 / To be implemented
+ ///
+ /// - 内部寄存器数据存储 / Internal register data storage
+ /// - 读写操作处理 / Read/write operation handling
+ /// - 异常响应生成 / Exception response generation
+ ///
+ ///
+ ///
+ ///
public class ModbusUtilityServer
{
-
+ // TODO: 实现 Modbus 服务器端功能
+ // TODO: Implement Modbus server-side functionality
+
+ // 建议的实现结构 / Suggested implementation structure:
+ // 1. 内部数据存储 (线圈和寄存器) / Internal data storage (coils and registers)
+ // 2. GetServerDatasAsync 实现 - 响应读请求 / GetServerDatasAsync implementation - respond to read requests
+ // 3. SetServerDatasAsync 实现 - 响应写请求 / SetServerDatasAsync implementation - respond to write requests
+ // 4. 异常处理 / Exception handling
+ // 5. 日志记录 / Logging
}
}
diff --git a/Modbus.Net/Modbus.Net.Siemens/AddressFormaterSiemens.cs b/Modbus.Net/Modbus.Net.Siemens/AddressFormaterSiemens.cs
index 4b78e02..16024a1 100644
--- a/Modbus.Net/Modbus.Net.Siemens/AddressFormaterSiemens.cs
+++ b/Modbus.Net/Modbus.Net.Siemens/AddressFormaterSiemens.cs
@@ -1,66 +1,220 @@
-namespace Modbus.Net.Siemens
+namespace Modbus.Net.Siemens
{
///
- /// Siemens地址格式化(Modbus.Net专用格式)
+ /// 西门子 S7 地址格式化器类 / Siemens S7 Address Formater Classes
+ ///
+ /// 实现西门子 PLC 地址的内部格式到字符串格式的转换
+ /// Implements conversion from Siemens PLC internal address format to string format
+ ///
+ /// 支持的格式 / Supported Formats:
+ ///
+ /// - Modbus.Net 格式 - "DB1 0", "I 0.0", "MW100" 等 / Modbus.Net format
+ /// - 西门子标准格式 - "DB1.DBW0", "I0.0", "MW100" 等 / Siemens standard format
+ ///
+ ///
+ ///
+ /// 地址组成 / Address Components:
+ ///
+ /// - Area - 区域标识 (DB/I/Q/M 等) / Area identifier
+ /// - Address - 地址偏移 / Address offset
+ /// - SubAddress - 子地址 (位偏移) / Sub-address (bit offset)
+ ///
+ ///
+ ///
+ ///
+
+ #region Modbus.Net 格式地址格式化器 / Modbus.Net Format Address Formater
+
+ ///
+ /// Siemens 地址格式化器(Modbus.Net 专用格式) / Siemens Address Formater (Modbus.Net Format)
+ ///
+ /// 使用空格分隔的格式:区域 地址 [.子地址]
+ /// Uses space-separated format: Area Address [.SubAddress]
+ ///
+ /// 格式化示例 / Formatting Examples:
+ ///
+ /// - Area="DB1", Address=0 → "DB1 0"
+ /// - Area="I", Address=0, SubAddress=0 → "I 0.0"
+ /// - Area="M", Address=100, SubAddress=0 → "M 100"
+ ///
+ ///
+ ///
+ /// 使用场景 / Use Cases:
+ ///
+ /// - Modbus.Net 内部地址表示 / Modbus.Net internal address representation
+ /// - 配置文件中的地址格式 / Address format in configuration files
+ ///
+ ///
+ ///
///
public class AddressFormaterSiemens : AddressFormater
{
///
- /// 编码地址
+ /// 编码地址 (无子地址) / Encode Address (without Sub-Address)
+ ///
+ /// 将区域和地址转换为字符串格式
+ /// Convert area and address to string format
+ ///
+ /// 格式 / Format:
+ /// Area + " " + Address
+ ///
+ ///
+ /// 示例 / Examples:
+ ///
+ /// - "DB1" + " " + 0 → "DB1 0"
+ /// - "I" + " " + 10 → "I 10"
+ ///
+ ///
+ ///
///
- /// 地址所在的数据区域
- /// 地址
- /// 编码后的地址
+ ///
+ /// 地址所在的数据区域 / Data Area
+ ///
+ /// 如 "DB1", "I", "Q", "M" 等
+ /// e.g., "DB1", "I", "Q", "M", etc.
+ ///
+ ///
+ /// 地址 / Address
+ /// 编码后的地址 / Encoded Address
public override string FormatAddress(string area, int address)
{
return area + " " + address;
}
///
- /// 编码地址
+ /// 编码地址 (带子地址) / Encode Address (with Sub-Address)
+ ///
+ /// 将区域、地址和子地址转换为字符串格式
+ /// Convert area, address and sub-address to string format
+ ///
+ /// 格式 / Format:
+ /// Area + " " + Address + "." + SubAddress
+ ///
+ ///
+ /// 示例 / Examples:
+ ///
+ /// - "I" + " " + 0 + "." + 0 → "I 0.0"
+ /// - "DB1" + " " + 10 + "." + 3 → "DB1 10.3"
+ ///
+ ///
+ ///
///
- /// 地址所在的数据区域
- /// 地址
- /// 子地址
- /// 编码后的地址
+ /// 地址所在的数据区域 / Data Area
+ /// 地址 / Address
+ ///
+ /// 子地址 (位偏移) / Sub-Address (Bit Offset)
+ ///
+ /// 范围:0-7
+ /// Range: 0-7
+ ///
+ ///
+ /// 编码后的地址 / Encoded Address
public override string FormatAddress(string area, int address, int subAddress)
{
return area + " " + address + "." + subAddress;
}
}
+ #endregion
+
+ #region 西门子标准格式地址格式化器 / Siemens Standard Format Address Formater
+
///
- /// Siemens地址格式化(Siemens格式)
+ /// Siemens 地址格式化器(西门子标准格式) / Siemens Address Formater (Siemens Standard Format)
+ ///
+ /// 使用西门子标准格式:区域地址 [.子地址]
+ /// Uses Siemens standard format: AreaAddress [.SubAddress]
+ ///
+ /// 格式化示例 / Formatting Examples:
+ ///
+ /// - Area="DB1", Address=0 → "DB1.DB0"
+ /// - Area="DB1", Address=0, SubAddress=0 → "DB1.DBX0.0"
+ /// - Area="I", Address=0, SubAddress=0 → "I0.0"
+ /// - Area="M", Address=100 → "M100"
+ ///
+ ///
+ ///
+ /// DB 块特殊处理 / DB Block Special Handling:
+ ///
+ /// - 添加 "DB" 前缀到地址 / Add "DB" prefix to address
+ /// - 例如:DB1 块,地址 0 → "DB1.DB0"
+ ///
+ ///
+ ///
///
public class AddressFormaterSimenseStandard : AddressFormater
{
///
- /// 编码地址
+ /// 编码地址 (无子地址) / Encode Address (without Sub-Address)
+ ///
+ /// 将区域和地址转换为西门子标准格式字符串
+ /// Convert area and address to Siemens standard format string
+ ///
+ /// DB 块处理 / DB Block Handling:
+ ///
+ /// - 格式:
Area + "." + "DB" + Address
+ /// - 例如:"DB1" + "." + "DB" + 0 → "DB1.DB0"
+ ///
+ ///
+ ///
+ /// 普通区域处理 / Normal Area Handling:
+ ///
+ /// - 格式:
Area + Address
+ /// - 例如:"I" + 0 → "I0"
+ ///
+ ///
+ ///
///
- /// 地址所在的数据区域
- /// 地址
- /// 编码后的地址
+ /// 地址所在的数据区域 / Data Area
+ /// 地址 / Address
+ /// 编码后的地址 / Encoded Address
public override string FormatAddress(string area, int address)
{
+ // DB 块特殊处理 / DB block special handling
if (area.Length > 1 &&
area.ToUpper().Substring(0, 2) == "DB")
return area.ToUpper() + "." + "DB" + address;
+
+ // 普通区域处理 / Normal area handling
return area.ToUpper() + address;
}
///
- /// 编码地址
+ /// 编码地址 (带子地址) / Encode Address (with Sub-Address)
+ ///
+ /// 将区域、地址和子地址转换为西门子标准格式字符串
+ /// Convert area, address and sub-address to Siemens standard format string
+ ///
+ /// DB 块处理 / DB Block Handling:
+ ///
+ /// - 格式:
Area + "." + "DB" + Address + "." + SubAddress
+ /// - 例如:"DB1" + "." + "DB" + 0 + "." + 0 → "DB1.DBX0.0"
+ ///
+ ///
+ ///
+ /// 普通区域处理 / Normal Area Handling:
+ ///
+ /// - 格式:
Area + Address + "." + SubAddress
+ /// - 例如:"I" + 0 + "." + 0 → "I0.0"
+ ///
+ ///
+ ///
///
- /// 地址所在的数据区域
- /// 地址
- /// 子地址
- /// 编码后的地址
+ /// 地址所在的数据区域 / Data Area
+ /// 地址 / Address
+ /// 子地址 (位偏移) / Sub-Address (Bit Offset)
+ /// 编码后的地址 / Encoded Address
public override string FormatAddress(string area, int address, int subAddress)
{
+ // DB 块特殊处理 / DB block special handling
if (area.Length > 1 &&
area.ToUpper().Substring(0, 2) == "DB")
return area.ToUpper() + "." + "DB" + address + "." + subAddress;
+
+ // 普通区域处理 / Normal area handling
return area.ToUpper() + address + "." + subAddress;
}
}
-}
\ No newline at end of file
+
+ #endregion
+}
diff --git a/Modbus.Net/Modbus.Net.Siemens/AddressTranslatorSiemens.cs b/Modbus.Net/Modbus.Net.Siemens/AddressTranslatorSiemens.cs
index 0129773..9ed54c9 100644
--- a/Modbus.Net/Modbus.Net.Siemens/AddressTranslatorSiemens.cs
+++ b/Modbus.Net/Modbus.Net.Siemens/AddressTranslatorSiemens.cs
@@ -1,107 +1,268 @@
-using System;
+using System;
using System.Collections.Generic;
namespace Modbus.Net.Siemens
{
///
- /// 地址翻译器(Modbus.Net格式)
+ /// 西门子地址翻译器类 / Siemens Address Translator Classes
+ ///
+ /// 实现西门子 PLC 地址字符串到内部地址结构的转换
+ /// Implements conversion from Siemens PLC address strings to internal address structure
+ ///
+ /// 支持的地址格式 / Supported Address Formats:
+ ///
+ /// - DB 块地址 - "DB1.DBW0", "DB1.DBB0", "DB1.DBD0", "DB1.DBX0.0"
+ /// - 输入映像区 - "I0.0", "IB0", "IW0", "ID0"
+ /// - 输出映像区 - "Q0.0", "QB0", "QW0", "QD0"
+ /// - 位存储区 - "M0.0", "MB0", "MW0", "MD0"
+ /// - 数据块 - "DB1", "DB2" 等 / Data blocks
+ ///
+ ///
+ ///
+ /// 区域代码映射 / Area Code Mapping:
+ ///
+ /// - S (0x04) - 系统存储区 / System memory
+ /// - SM (0x05) - 特殊存储区 / Special memory
+ /// - AI (0x06) - 模拟输入 / Analog input
+ /// - AQ (0x07) - 模拟输出 / Analog output
+ /// - C (0x1E) - 计数器 / Counter
+ /// - T (0x1F) - 计时器 / Timer
+ /// - HC (0x20) - 高速计数器 / High-speed counter
+ /// - I (0x81) - 输入映像区 / Input image
+ /// - Q (0x82) - 输出映像区 / Output image
+ /// - M (0x83) - 位存储区 / Memory
+ /// - DB (0x84) - 数据块 / Data block
+ /// - V (0x184) - 变量存储区 (S7-200) / Variable memory (S7-200)
+ ///
+ ///
+ ///
+ ///
+
+ #region Modbus.Net 格式地址翻译器 / Modbus.Net Format Address Translator
+
+ ///
+ /// 地址翻译器(Modbus.Net 格式) / Address Translator (Modbus.Net Format)
+ ///
+ /// 使用空格分隔的格式:区域 地址
+ /// Uses space-separated format: Area Address
+ ///
+ /// 地址格式 / Address Format:
+ ///
+ /// - "DB1 0" - DB1 块,偏移 0 / DB1 block, offset 0
+ /// - "I 0" - 输入区,地址 0 / Input area, address 0
+ /// - "Q 100" - 输出区,地址 100 / Output area, address 100
+ /// - "M 50.2" - 存储区,地址 50,位 2 / Memory area, address 50, bit 2
+ ///
+ ///
+ ///
+ /// DB 块特殊处理 / DB Block Special Handling:
+ ///
+ /// - Area = DB 编号 * 256 + 0x84
+ /// - 例如:DB1 → Area = 1 * 256 + 0x84 = 0x0184
+ ///
+ ///
+ ///
///
public class AddressTranslatorSiemens : AddressTranslator
{
///
- /// 区域的翻译字典
+ /// 区域代码翻译字典 / Area Code Translation Dictionary
+ ///
+ /// 存储区域字符串到代码的映射
+ /// Stores mapping from area string to code
+ ///
///
protected Dictionary AreaCodeDictionary;
///
- /// 构造函数
+ /// 构造函数 / Constructor
+ ///
+ /// 初始化区域代码字典
+ /// Initialize area code dictionary
+ ///
///
public AddressTranslatorSiemens()
{
AreaCodeDictionary = new Dictionary
{
- {"S", 0x04},
- {"SM", 0x05},
- {"AI", 0x06},
- {"AQ", 0x07},
- {"C", 0x1E},
- {"T", 0x1F},
- {"HC", 0x20},
- {"I", 0x81},
- {"Q", 0x82},
- {"M", 0x83},
- {"DB", 0x84},
- {"V", 0x184}
+ {"S", 0x04}, // 系统存储区 / System memory
+ {"SM", 0x05}, // 特殊存储区 / Special memory
+ {"AI", 0x06}, // 模拟输入 / Analog input
+ {"AQ", 0x07}, // 模拟输出 / Analog output
+ {"C", 0x1E}, // 计数器 / Counter
+ {"T", 0x1F}, // 计时器 / Timer
+ {"HC", 0x20}, // 高速计数器 / High-speed counter
+ {"I", 0x81}, // 输入映像区 / Input image
+ {"Q", 0x82}, // 输出映像区 / Output image
+ {"M", 0x83}, // 位存储区 / Memory
+ {"DB", 0x84}, // 数据块 / Data block
+ {"V", 0x184} // 变量存储区 (S7-200) / Variable memory (S7-200)
};
}
///
- /// 地址转换
+ /// 地址转换 / Address Translate
+ ///
+ /// 将格式化的地址字符串翻译为 AddressDef 对象
+ /// Translate formatted address string to AddressDef object
+ ///
+ /// 处理流程 / Processing Flow:
+ ///
+ /// - 转为大写 / Convert to uppercase
+ /// - 按空格分割 / Split by space
+ /// - 提取区域 (head) 和地址 (tail) / Extract area (head) and address (tail)
+ /// - 处理子地址 (位偏移) / Handle sub-address (bit offset)
+ /// - DB 块特殊处理 / DB block special handling
+ /// - 创建并返回 AddressDef / Create and return AddressDef
+ ///
+ ///
+ ///
///
- /// 格式化的地址
- /// 是否为读取,是为读取,否为写入
- /// 翻译后的地址
+ ///
+ /// 格式化的地址 / Formatted Address
+ ///
+ /// 示例 / Examples:
+ ///
+ /// - "DB1 0" - DB1 块,偏移 0
+ /// - "I 0.2" - 输入区,地址 0,位 2
+ /// - "M 100" - 存储区,地址 100
+ ///
+ ///
+ ///
+ ///
+ /// 是否为读取 / Whether it's Read
+ ///
+ /// true: 读取操作 / Read operation
+ /// false: 写入操作 / Write operation
+ ///
+ ///
+ ///
+ /// 翻译后的地址 / Translated Address
+ ///
+ /// AddressDef 包含:
+ /// AddressDef contains:
+ ///
+ /// - AreaString: 区域字符串 / Area string
+ /// - Area: 区域代码 / Area code
+ /// - Address: 地址 / Address
+ /// - SubAddress: 子地址 (位偏移) / Sub-address (bit offset)
+ ///
+ ///
+ ///
public override AddressDef AddressTranslate(string address, bool isRead)
{
+ // 转为大写 / Convert to uppercase
address = address.ToUpper();
+
+ // 按空格分割 / Split by space
var splitString = address.Split(' ');
- var head = splitString[0];
- var tail = splitString[1];
+ var head = splitString[0]; // 区域 / Area
+ var tail = splitString[1]; // 地址 / Address
+
+ // 处理子地址 (位偏移) / Handle sub-address (bit offset)
string sub;
if (tail.Contains("."))
{
var splitString2 = tail.Split('.');
- sub = splitString2[1];
- tail = splitString2[0];
+ sub = splitString2[1]; // 子地址 / Sub-address
+ tail = splitString2[0]; // 主地址 / Main address
}
else
{
- sub = "0";
+ sub = "0"; // 默认子地址 / Default sub-address
}
+
+ // DB 块特殊处理 / DB block special handling
if (head.Length > 1 && head.Substring(0, 2) == "DB")
{
- head = head.Substring(2);
+ head = head.Substring(2); // 移除 "DB" 前缀 / Remove "DB" prefix
return new AddressDef
{
- AreaString = "DB" + head,
- Area = int.Parse(head) * 256 + AreaCodeDictionary["DB"],
- Address = int.Parse(tail),
- SubAddress = int.Parse(sub)
+ AreaString = "DB" + head, // 区域字符串 / Area string
+ Area = int.Parse(head) * 256 + AreaCodeDictionary["DB"], // DB 块代码 / DB block code
+ Address = int.Parse(tail), // 地址 / Address
+ SubAddress = int.Parse(sub) // 子地址 / Sub-address
};
}
- return
- new AddressDef
- {
- AreaString = head,
- Area = AreaCodeDictionary[head],
- Address = int.Parse(tail),
- SubAddress = int.Parse(sub)
- };
+
+ // 普通区域处理 / Normal area handling
+ return new AddressDef
+ {
+ AreaString = head,
+ Area = AreaCodeDictionary[head],
+ Address = int.Parse(tail),
+ SubAddress = int.Parse(sub)
+ };
}
///
- /// 获取区域中的单个地址占用的字节长度
+ /// 获取区域字节长度 / Get Area Byte Length
+ ///
+ /// 返回区域中单个地址占用的字节长度
+ /// Returns byte length per address in area
+ ///
+ /// 西门子地址默认返回 1 字节
+ /// Siemens address defaults to 1 byte
+ ///
+ ///
///
- /// 区域名称
- /// 字节长度
+ /// 区域名称 / Area Name
+ /// 字节长度 / Byte Length
public override double GetAreaByteLength(string area)
{
return 1;
}
}
+ #endregion
+
+ #region 西门子标准格式地址翻译器 / Siemens Standard Format Address Translator
+
///
- /// 地址翻译器(Siemens格式)
+ /// 地址翻译器(西门子标准格式) / Address Translator (Siemens Standard Format)
+ ///
+ /// 使用西门子标准格式:区域地址.子地址
+ /// Uses Siemens standard format: AreaAddress.SubAddress
+ ///
+ /// 支持的格式 / Supported Formats:
+ ///
+ /// - "DB1.DBW0" - DB1 块,字地址 0 / DB1 block, word address 0
+ /// - "DB1.DBB0" - DB1 块,字节地址 0 / DB1 block, byte address 0
+ /// - "DB1.DBD0" - DB1 块,双字地址 0 / DB1 block, double word address 0
+ /// - "DB1.DBX0.0" - DB1 块,位地址 0.0 / DB1 block, bit address 0.0
+ /// - "I0.0" - 输入区,位地址 0.0 / Input area, bit address 0.0
+ /// - "QW10" - 输出区,字地址 10 / Output area, word address 10
+ /// - "MW100" - 存储区,字地址 100 / Memory area, word address 100
+ ///
+ ///
+ ///
+ /// 地址类型后缀 / Address Type Suffixes:
+ ///
+ /// - X - 位 / Bit (可选)
+ /// - B - 字节 / Byte
+ /// - W - 字 / Word
+ /// - D - 双字 / Double word
+ ///
+ ///
+ ///
///
public class AddressTranslatorSimenseStandard : AddressTranslator
{
///
- /// 区域的翻译字典
+ /// 区域代码翻译字典 / Area Code Translation Dictionary
+ ///
+ /// 存储区域字符串到代码的映射
+ /// Stores mapping from area string to code
+ ///
///
protected Dictionary AreaCodeDictionary;
///
- /// 构造函数
+ /// 构造函数 / Constructor
+ ///
+ /// 初始化区域代码字典
+ /// Initialize area code dictionary
+ ///
///
public AddressTranslatorSimenseStandard()
{
@@ -123,21 +284,55 @@ namespace Modbus.Net.Siemens
}
///
- /// 地址转换
+ /// 地址转换 / Address Translate
+ ///
+ /// 将西门子标准格式地址翻译为 AddressDef 对象
+ /// Translate Siemens standard format address to AddressDef object
+ ///
+ /// DB 块格式 / DB Block Format:
+ ///
+ /// - "DB1.DBW0" → AreaString="DB1", Area=0x0184, Address=0
+ /// - "DB1.DBB10" → AreaString="DB1", Area=0x0184, Address=10
+ ///
+ ///
+ ///
+ /// 普通区域格式 / Normal Area Format:
+ ///
+ /// - "I0.0" → AreaString="I", Area=0x81, Address=0, SubAddress=0
+ /// - "MW100" → AreaString="M", Area=0x83, Address=100, SubAddress=0
+ ///
+ ///
+ ///
///
- /// 格式化的地址
- /// 是否为读取,是为读取,否为写入
- /// 翻译后的地址
+ ///
+ /// 格式化的地址 / Formatted Address
+ ///
+ /// 西门子标准格式 / Siemens standard format:
+ ///
+ /// - "DB1.DBW0"
+ /// - "I0.0"
+ /// - "MW100"
+ ///
+ ///
+ ///
+ /// 是否为读取 / Whether it's Read
+ /// 翻译后的地址 / Translated Address
public override AddressDef AddressTranslate(string address, bool isRead)
{
address = address.ToUpper();
+
+ // DB 块地址处理 / DB block address handling
if (address.Substring(0, 2) == "DB")
{
var addressSplit = address.Split('.');
if (addressSplit.Length != 2 && addressSplit.Length != 3) throw new FormatException();
- addressSplit[0] = addressSplit[0].Substring(2);
+
+ addressSplit[0] = addressSplit[0].Substring(2); // 移除 "DB" 前缀 / Remove "DB" prefix
+
+ // 移除数据类型前缀 (DBW/DBB/DBD/DBX) / Remove data type prefix
if (addressSplit[1].Substring(0, 2) == "DB")
addressSplit[1] = addressSplit[1].Substring(2);
+
return new AddressDef
{
AreaString = "DB" + addressSplit[0],
@@ -146,13 +341,20 @@ namespace Modbus.Net.Siemens
SubAddress = addressSplit.Length == 2 ? 0 : int.Parse(addressSplit[2])
};
}
+
+ // 普通区域地址处理 / Normal area address handling
+ // 查找第一个数字的位置 / Find first digit position
var i = 0;
int t;
while (!int.TryParse(address[i].ToString(), out t) && i < address.Length)
i++;
+
if (i == 0 || i >= address.Length) throw new FormatException();
- var head = address.Substring(0, i);
- var tail = address.Substring(i).Split('.');
+
+ // 分割区域和地址 / Split area and address
+ var head = address.Substring(0, i); // 区域 / Area
+ var tail = address.Substring(i).Split('.'); // 地址和子地址 / Address and sub-address
+
return new AddressDef
{
AreaString = head,
@@ -163,13 +365,19 @@ namespace Modbus.Net.Siemens
}
///
- /// 获取区域中的单个地址占用的字节长度
+ /// 获取区域字节长度 / Get Area Byte Length
+ ///
+ /// 返回区域中单个地址占用的字节长度
+ /// Returns byte length per address in area
+ ///
///
- /// 区域名称
- /// 字节长度
+ /// 区域名称 / Area Name
+ /// 字节长度 / Byte Length
public override double GetAreaByteLength(string area)
{
return 1;
}
}
-}
\ No newline at end of file
+
+ #endregion
+}
diff --git a/Modbus.Net/Modbus.Net.Siemens/SiemensController.cs b/Modbus.Net/Modbus.Net.Siemens/SiemensController.cs
index e0bb902..95610da 100644
--- a/Modbus.Net/Modbus.Net.Siemens/SiemensController.cs
+++ b/Modbus.Net/Modbus.Net.Siemens/SiemensController.cs
@@ -1,29 +1,118 @@
-using System.Collections.Generic;
+using System.Collections.Generic;
namespace Modbus.Net.Siemens
{
///
- /// 西门子Ppi协议控制器
+ /// 西门子 S7 协议控制器类 / Siemens S7 Protocol Controller Classes
+ ///
+ /// 实现西门子 S7 协议的控制器,管理消息发送和响应匹配
+ /// Implements controllers for Siemens S7 protocol, managing message sending and response matching
+ ///
+ /// 主要控制器 / Main Controllers:
+ ///
+ /// - SiemensPpiController - PPI 串口控制器 / PPI serial controller
+ /// - SiemensTcpController - TCP 以太网控制器 / TCP Ethernet controller
+ ///
+ ///
+ ///
+ ///
+
+ #region PPI 协议控制器 / PPI Protocol Controller
+
+ ///
+ /// 西门子 PPI 协议控制器 / Siemens PPI Protocol Controller
+ ///
+ /// 用于 S7-200 系列 PLC 的 PPI 串口通信控制
+ /// Used for PPI serial communication control of S7-200 series PLC
+ ///
+ /// 控制器特点 / Controller Characteristics:
+ ///
+ /// - 继承自 FifoController / Inherits from FifoController
+ /// - FIFO 顺序发送 / FIFO sequential sending
+ /// - FCS 校验 / FCS checksum
+ /// - 支持可变长度帧 / Supports variable length frames
+ ///
+ ///
+ ///
+ /// 帧长度计算 / Frame Length Calculation:
+ ///
+ /// - 启动字符 0x10: 固定 6 字节 / Start char 0x10: Fixed 6 bytes
+ /// - 启动字符 0xE5: 固定 1 字节 / Start char 0xE5: Fixed 1 byte
+ /// - 其他:根据长度字段计算 / Others: Calculate based on length field
+ ///
+ ///
+ ///
+ /// 使用示例 / Usage Example:
+ ///
+ /// // 创建 PPI 控制器 / Create PPI controller
+ /// var controller = new SiemensPpiController(
+ /// com: "COM1",
+ /// slaveAddress: 2
+ /// );
+ ///
+ /// // 添加到连接器 / Add to connector
+ /// connector.AddController(controller);
+ ///
+ ///
+ ///
///
public class SiemensPpiController : FifoController
{
///
- /// 构造函数
+ /// 构造函数 / Constructor
+ ///
+ /// 初始化 PPI 协议控制器,配置所有参数
+ /// Initialize PPI protocol controller with all parameters
+ ///
+ /// 配置参数 / Configuration Parameters:
+ ///
+ /// - FetchSleepTime: 从配置读取获取间隔 / Read from configuration
+ /// - LengthCalc: PPI 帧长度计算 / PPI frame length calculation
+ /// - CheckRightFunc: FCS 校验 / FCS checksum
+ /// - WaitingListCount: 从配置读取等待队列长度 / Read from configuration
+ ///
+ ///
+ ///
///
- /// 串口
- /// 从站号
+ ///
+ /// 串口名称 / Serial Port Name
+ ///
+ /// 如 "COM1", "COM2" 等
+ /// e.g., "COM1", "COM2", etc.
+ ///
+ ///
+ ///
+ /// 从站号 / Slave Address
+ ///
+ /// S7-200 的站地址,范围 0-126
+ /// S7-200 station address, range 0-126
+ ///
+ ///
public SiemensPpiController(string com, int slaveAddress) : base(
+ // 从配置读取获取间隔时间 / Read fetch interval time from configuration
int.Parse(ConfigurationReader.GetValue("COM:" + com + ":" + slaveAddress, "FetchSleepTime")),
+
+ // PPI 帧长度计算 / PPI frame length calculation
lengthCalc: content =>
{
+ // 启动字符 0x10: 固定 6 字节
+ // Start char 0x10: Fixed 6 bytes
if (content[0] == 0x10)
return 6;
+ // 启动字符 0xE5: 固定 1 字节 (确认字符)
+ // Start char 0xE5: Fixed 1 byte (acknowledge char)
else if (content[0] == 0xE5)
return 1;
+ // 其他帧:根据长度字段计算
+ // Other frames: Calculate based on length field
else
return DuplicateWithCount.GetDuplcateFunc(new List { 1 }, 6)(content);
},
+
+ // FCS 校验函数 / FCS check function
checkRightFunc: ContentCheck.FcsCheckRight,
+
+ // 从配置读取等待队列长度 / Read waiting list count from configuration
waitingListMaxCount: ConfigurationReader.GetValue("COM:" + com + ":" + slaveAddress, "WaitingListCount") != null ?
int.Parse(ConfigurationReader.GetValue("COM:" + com + ":" + slaveAddress, "WaitingListCount")) :
null
@@ -31,23 +120,100 @@ namespace Modbus.Net.Siemens
{ }
}
+ #endregion
+
+ #region TCP 协议控制器 / TCP Protocol Controller
+
///
- /// 西门子Tcp协议控制器
+ /// 西门子 TCP 协议控制器 / Siemens TCP Protocol Controller
+ ///
+ /// 用于 S7-1200/1500/300/400 系列 PLC 的 TCP 以太网通信控制
+ /// Used for TCP Ethernet communication control of S7-1200/1500/300/400 series PLC
+ ///
+ /// 控制器特点 / Controller Characteristics:
+ ///
+ /// - 继承自 MatchDirectlySendController / Inherits from MatchDirectlySendController
+ /// - 匹配发送,无需等待响应 / Match send, no need to wait for response
+ /// - 根据字节位置匹配请求响应 / Match request-response based on byte positions
+ ///
+ ///
+ ///
+ /// 匹配规则 / Matching Rules:
+ ///
+ /// - 位置 11,12: 事务 ID / Positions 11,12: Transaction ID
+ /// - 用于匹配请求和响应 / Used to match request and response
+ ///
+ ///
+ ///
+ /// 帧长度计算 / Frame Length Calculation:
+ ///
+ /// - 根据位置 2,3 的长度字段计算 / Calculate based on length field at positions 2,3
+ /// - 固定偏移:0 字节 / Fixed offset: 0 bytes
+ ///
+ ///
+ ///
+ /// 使用示例 / Usage Example:
+ ///
+ /// // 创建 TCP 控制器 / Create TCP controller
+ /// var controller = new SiemensTcpController(
+ /// ip: "192.168.1.100",
+ /// port: 102
+ /// );
+ ///
+ /// // 添加到连接器 / Add to connector
+ /// connector.AddController(controller);
+ ///
+ ///
+ ///
///
public class SiemensTcpController : MatchDirectlySendController
{
///
- /// 构造函数
+ /// 构造函数 / Constructor
+ ///
+ /// 初始化 TCP 协议控制器,配置所有参数
+ /// Initialize TCP protocol controller with all parameters
+ ///
+ /// 配置参数 / Configuration Parameters:
+ ///
+ /// - KeyMatches: 匹配规则 (位置 11,12 的事务 ID) / Match rules (transaction ID at positions 11,12)
+ /// - LengthCalc: TCP 帧长度计算 / TCP frame length calculation
+ /// - WaitingListCount: 从配置读取等待队列长度 / Read from configuration
+ ///
+ ///
+ ///
///
- /// ip地址
- /// 端口号
+ ///
+ /// IP 地址 / IP Address
+ ///
+ /// PLC 的 IP 地址
+ /// PLC IP address
+ ///
+ ///
+ ///
+ /// 端口号 / Port Number
+ ///
+ /// 西门子 PLC 默认端口:102
+ /// Siemens PLC default port: 102
+ ///
+ ///
public SiemensTcpController(string ip, int port) : base(
+ // 匹配规则:位置 11,12 的事务 ID
+ // Match rules: Transaction ID at positions 11,12
new ICollection<(int, int)>[] { new List<(int, int)> { (11, 11), (12, 12) } },
+
+ // TCP 帧长度计算 / TCP frame length calculation
+ // 根据位置 2,3 的长度字段计算
+ // Calculate based on length field at positions 2,3
lengthCalc: DuplicateWithCount.GetDuplcateFunc(new List { 2, 3 }, 0),
+
+ // 从配置读取等待队列长度 / Read waiting list count from configuration
waitingListMaxCount: ConfigurationReader.GetValue("TCP:" + ip + ":" + port, "WaitingListCount") != null ?
int.Parse(ConfigurationReader.GetValue("TCP:" + ip + ":" + port, "WaitingListCount")) :
null
)
{ }
}
+
+ #endregion
}
diff --git a/Modbus.Net/Modbus.Net.Siemens/SiemensMachine.cs b/Modbus.Net/Modbus.Net.Siemens/SiemensMachine.cs
index 08562d0..2c53a9e 100644
--- a/Modbus.Net/Modbus.Net.Siemens/SiemensMachine.cs
+++ b/Modbus.Net/Modbus.Net.Siemens/SiemensMachine.cs
@@ -1,53 +1,255 @@
-using System;
+using System;
using System.Collections.Generic;
namespace Modbus.Net.Siemens
{
///
- /// 西门子设备
+ /// 西门子 S7 设备类 / Siemens S7 Machine Class
+ ///
+ /// 提供西门子 S7 系列 PLC 的高级 API 封装
+ /// Provides high-level API encapsulation for Siemens S7 series PLC
+ ///
+ /// 支持的 PLC 型号 / Supported PLC Models:
+ ///
+ /// - S7-200 - 小型 PLC / Small PLC
+ /// - S7-200 Smart - 增强型 S7-200 / Enhanced S7-200
+ /// - S7-300 - 中型 PLC / Medium PLC
+ /// - S7-400 - 大型 PLC / Large PLC
+ /// - S7-1200 - 紧凑型 PLC / Compact PLC
+ /// - S7-1500 - 旗舰型 PLC / Flagship PLC
+ ///
+ ///
+ ///
+ /// 连接类型 / Connection Types:
+ ///
+ /// - SiemensType.Ppi - PPI 串口连接 (S7-200) / PPI serial connection
+ /// - SiemensType.Tcp - 以太网连接 (S7-1200/1500) / Ethernet connection
+ ///
+ ///
+ ///
+ /// 地址格式 / Address Formats:
+ ///
+ /// - "DB1.DBW0" - DB1 块,字地址 0 / DB1 block, word address 0
+ /// - "DB1.DBB0" - DB1 块,字节地址 0 / DB1 block, byte address 0
+ /// - "DB1.DBD0" - DB1 块,双字地址 0 / DB1 block, double word address 0
+ /// - "DB1.DBX0.0" - DB1 块,位地址 0.0 / DB1 block, bit address 0.0
+ /// - "I0.0" - 输入映像区,位 0 / Input image, bit 0
+ /// - "Q0.0" - 输出映像区,位 0 / Output image, bit 0
+ /// - "M0.0" - 位存储区,位 0 / Memory, bit 0
+ ///
+ ///
+ ///
+ /// 使用示例 / Usage Example:
+ ///
+ /// // 定义地址配置 / Define address configuration
+ /// var addresses = new List<AddressUnit>
+ /// {
+ /// new AddressUnit
+ /// {
+ /// Id = "1",
+ /// Area = "DB1",
+ /// Address = 0,
+ /// CommunicationTag = "Temperature",
+ /// DataType = typeof(ushort),
+ /// Name = "进水温度",
+ /// Unit = "°C"
+ /// },
+ /// new AddressUnit
+ /// {
+ /// Id = "2",
+ /// Area = "DB1",
+ /// Address = 2,
+ /// CommunicationTag = "Pressure",
+ /// DataType = typeof(ushort),
+ /// Name = "进水压力",
+ /// Unit = "MPa"
+ /// }
+ /// };
+ ///
+ /// // 创建 S7-1200 设备实例 / Create S7-1200 machine instance
+ /// var machine = new SiemensMachine<string, string>(
+ /// id: "PLC1",
+ /// alias: "1#PLC",
+ /// connectionType: SiemensType.Tcp,
+ /// connectionString: "192.168.1.100:102",
+ /// model: SiemensMachineModel.S7_1200,
+ /// getAddresses: addresses,
+ /// keepConnect: true,
+ /// slaveAddress: 1,
+ /// masterAddress: 0,
+ /// src: 0x01, // 本地 TSAP / Local TSAP
+ /// dst: 0x01 // 远程 TSAP / Remote TSAP
+ /// );
+ ///
+ /// // 连接 PLC / Connect to PLC
+ /// await machine.ConnectAsync();
+ ///
+ /// // 读取数据 (按通信标签) / Read data (by communication tag)
+ /// var result = await machine.GetDatasAsync(MachineDataType.CommunicationTag);
+ /// if (result.IsSuccess)
+ /// {
+ /// double temperature = result.Datas["Temperature"].DeviceValue;
+ /// double pressure = result.Datas["Pressure"].DeviceValue;
+ /// Console.WriteLine($"温度:{temperature}°C, 压力:{pressure}MPa");
+ /// }
+ ///
+ ///
+ ///
///
- public class SiemensMachine : BaseMachine where TKey : IEquatable
+ ///
+ /// 设备 ID 类型 / Device ID Type
+ ///
+ /// 通常是 string 或 int
+ /// Usually string or int
+ ///
+ ///
+ ///
+ /// AddressUnit 的 ID 类型 / AddressUnit ID Type
+ ///
+ /// 通常是 string 或 int
+ /// Usually string or int
+ ///
+ ///
+ public class SiemensMachine : BaseMachine
+ where TKey : IEquatable
where TUnitKey : IEquatable
{
///
- /// 构造函数
+ /// 构造函数 (带保持连接参数) / Constructor (with Keep-Alive Parameter)
+ ///
+ /// 初始化西门子 S7 设备实例,配置所有通信参数
+ /// Initialize Siemens S7 machine instance with all communication parameters
+ ///
+ /// 初始化内容 / Initialization Contents:
+ ///
+ /// - 创建 SiemensUtility 实例 / Create SiemensUtility instance
+ /// - 配置地址格式化器 / Configure address formater
+ /// - 配置地址组合器 / Configure address combiner
+ /// - 设置最大通信长度 100 字节 / Set max communication length 100 bytes
+ ///
+ ///
+ ///
+ /// TSAP 配置说明 / TSAP Configuration Notes:
+ ///
+ /// - S7-200: src=本地栈号,dst=远程栈号 / src=local rack, dst=remote rack
+ /// - S7-300/400: src=0x4b54(固定),dst=0x0300+槽号 / dst=0x0300+slot
+ /// - S7-1200/1500: src=0x1011(固定),dst=0x0300+槽号 / dst=0x0300+slot
+ /// - S7-200 Smart: src=0x0101(固定),dst=0x0101(固定) / Fixed values
+ ///
+ ///
+ ///
///
- /// 设备id号
- /// 连接类型
- /// 连接地址
- /// 设备类型
- /// 读写的地址
- /// 是否保持连接
- /// 从站号
- /// 主站号
- /// 本机模块位,0到7,仅200使用,其它型号不要填写
- /// PLC模块位,0到7,仅200使用,其它型号不要填写
+ ///
+ /// 设备的 ID 号 / Device ID Number
+ ///
+ /// 唯一标识设备的字符串或数字
+ /// String or number uniquely identifying the device
+ ///
+ ///
+ ///
+ /// 设备别名 / Device Alias
+ ///
+ /// 人类可读的设备名称
+ /// Human-readable device name
+ ///
+ ///
+ ///
+ /// 连接类型 / Connection Type
+ ///
+ /// SiemensType.Ppi - PPI 串口连接
+ /// SiemensType.Tcp - 以太网连接
+ ///
+ ///
+ ///
+ /// 连接地址 / Connection Address
+ ///
+ /// TCP: "192.168.1.100:102"
+ /// PPI: "COM1" 或 "COM1,9600,None,8,1"
+ ///
+ ///
+ ///
+ /// 设备类型 / Device Model
+ ///
+ /// SiemensMachineModel 枚举值
+ /// SiemensMachineModel enum value
+ ///
+ ///
+ ///
+ /// 读写的地址 / Addresses to Read/Write
+ ///
+ /// AddressUnit 列表,定义所有需要通信的地址
+ /// AddressUnit list defining all addresses to communicate
+ ///
+ ///
+ ///
+ /// 是否保持连接 / Whether to Keep Connection
+ ///
+ /// true: 长连接,性能更好 / true: Long connection, better performance
+ /// false: 短连接,每次操作重新连接 / false: Short connection, reconnect each operation
+ ///
+ ///
+ ///
+ /// 从站号 / Slave Address
+ /// PLC 的站地址 / PLC station address
+ ///
+ ///
+ /// 主站号 / Master Address
+ /// PC/上位机的站地址 / PC/HMI station address
+ ///
+ ///
+ /// 本机模块位 / Local Module Position
+ ///
+ /// 0 到 7,仅 S7-200 使用,其它型号不要填写
+ /// 0 to 7, only for S7-200, leave empty for other models
+ ///
+ ///
+ ///
+ /// PLC 模块位 / PLC Module Position
+ ///
+ /// 0 到 7,仅 S7-200 使用,其它型号不要填写
+ /// 0 to 7, only for S7-200, leave empty for other models
+ ///
+ ///
public SiemensMachine(TKey id, string alias, SiemensType connectionType, string connectionString, SiemensMachineModel model,
IEnumerable> getAddresses, bool keepConnect, byte slaveAddress, byte masterAddress, byte src = 1, byte dst = 0)
: base(id, alias, getAddresses, keepConnect, slaveAddress, masterAddress)
{
+ // 创建 Siemens Utility 实例 / Create Siemens Utility instance
BaseUtility = new SiemensUtility(connectionType, connectionString, model, slaveAddress, masterAddress, src, dst);
+
+ // 配置西门子地址格式化器 / Configure Siemens address formater
AddressFormater = new AddressFormaterSiemens();
+
+ // 配置地址组合器 (读取) / Configure address combiner (read)
+ // 使用连续地址组合器,最大长度 100 字节
+ // Use continuous address combiner, max length 100 bytes
AddressCombiner = new AddressCombinerContinus(AddressTranslator, 100);
+
+ // 配置地址组合器 (写入) / Configure address combiner (write)
AddressCombinerSet = new AddressCombinerContinus(AddressTranslator, 100);
}
///
- /// 构造函数
+ /// 构造函数 (默认保持连接=true) / Constructor (Default Keep-Alive=true)
+ ///
+ /// 简化版本的构造函数,默认保持连接
+ /// Simplified constructor version with default keep-alive
+ ///
///
- /// 设备id号
- /// 连接类型
- /// 连接地址
- /// 设备类型
- /// 读写的地址
- /// 从站号
- /// 主站号
- /// 本机模块位,0到7,仅200使用,其它型号不要填写
- /// PLC模块位,0到7,仅200使用,其它型号不要填写
+ /// 设备的 ID 号 / Device ID Number
+ /// 设备别名 / Device Alias
+ /// 连接类型 / Connection Type
+ /// 连接地址 / Connection Address
+ /// 设备类型 / Device Model
+ /// 读写的地址 / Addresses to Read/Write
+ /// 从站号 / Slave Address
+ /// 主站号 / Master Address
+ /// 本机模块位 / Local Module Position
+ /// PLC 模块位 / PLC Module Position
public SiemensMachine(TKey id, string alias, SiemensType connectionType, string connectionString, SiemensMachineModel model,
IEnumerable> getAddresses, byte slaveAddress, byte masterAddress, byte src = 1, byte dst = 0)
: this(id, alias, connectionType, connectionString, model, getAddresses, true, slaveAddress, masterAddress, src, dst)
{
}
}
-}
\ No newline at end of file
+}
diff --git a/Modbus.Net/Modbus.Net.Siemens/SiemensPpiProtocol.cs b/Modbus.Net/Modbus.Net.Siemens/SiemensPpiProtocol.cs
index 422e378..b020da1 100644
--- a/Modbus.Net/Modbus.Net.Siemens/SiemensPpiProtocol.cs
+++ b/Modbus.Net/Modbus.Net.Siemens/SiemensPpiProtocol.cs
@@ -1,74 +1,223 @@
-using Nito.AsyncEx;
+using Nito.AsyncEx;
using System.Threading.Tasks;
namespace Modbus.Net.Siemens
{
///
- /// 西门子Ppi协议
+ /// 西门子 S7 PPI 协议类 / Siemens S7 PPI Protocol Class
+ ///
+ /// 实现西门子 S7-200 系列 PLC 的 PPI (Point-to-Point Interface) 串口通信协议
+ /// Implements PPI (Point-to-Point Interface) serial communication protocol for Siemens S7-200 series PLC
+ ///
+ /// 协议特点 / Protocol Characteristics:
+ ///
+ /// - 基于 RS-485 串口 / Based on RS-485 serial
+ /// - 主从模式 / Master-slave mode
+ /// - 令牌传递机制 / Token passing mechanism
+ /// - 波特率:9.6K/19.2K/187.5K / Baud rate: 9.6K/19.2K/187.5K
+ /// - 适用于 S7-200 系列 / Suitable for S7-200 series
+ ///
+ ///
+ ///
+ /// 与 TCP 协议的区别 / Difference from TCP Protocol:
+ ///
+ /// - PPI - 串口通信,无需连接建立 / Serial communication, no connection establishment
+ /// - TCP - 以太网通信,需要连接建立和参数协商 / Ethernet communication, requires connection establishment and parameter negotiation
+ ///
+ ///
+ ///
+ /// 使用示例 / Usage Example:
+ ///
+ /// // 创建 S7-200 PPI 协议实例 / Create S7-200 PPI protocol instance
+ /// var protocol = new SiemensPpiProtocol(
+ /// "COM1", // 串口名称 / Serial port name
+ /// slaveAddress: 2, // S7-200 站地址
+ /// masterAddress: 0 // PC/上位机站地址
+ /// );
+ ///
+ /// // 或者从配置读取 / Or read from configuration
+ /// var protocolFromConfig = new SiemensPpiProtocol(slaveAddress: 2, masterAddress: 0);
+ /// // 配置项:COM:Siemens:COM = "COM1"
+ ///
+ /// // 连接设备 (PPI 串口无需真正连接) / Connect to device (PPI serial doesn't need real connection)
+ /// await protocol.ConnectAsync();
+ ///
+ /// // 读取 V 区数据 / Read V memory data
+ /// var inputStruct = new ReadDataSiemensInputStruct(...);
+ /// var outputStruct = await protocol.SendReceiveAsync<ReadDataSiemensOutputStruct>(
+ /// protocol[typeof(ReadDataSiemensProtocol)],
+ /// inputStruct
+ /// );
+ ///
+ ///
+ ///
///
public class SiemensPpiProtocol : SiemensProtocol
{
+ ///
+ /// 串口名称 / Serial Port Name
+ ///
+ /// 如 "COM1", "COM2" 等
+ /// e.g., "COM1", "COM2", etc.
+ ///
+ ///
private readonly string _com;
+
+ ///
+ /// 异步锁 / Async Lock
+ ///
+ /// 用于保护并发连接操作
+ /// Used to protect concurrent connection operations
+ ///
+ ///
private readonly AsyncLock _lock = new AsyncLock();
///
- /// 构造函数
+ /// 构造函数 (从配置读取串口) / Constructor (Read COM Port from Configuration)
+ ///
+ /// 从配置文件读取串口名称创建 PPI 协议实例
+ /// Create PPI protocol instance with COM port name read from configuration file
+ ///
+ /// 配置项 / Configuration Item:
+ /// COM:Siemens:COM = "COM1"
+ ///
+ ///
///
- /// 从站号
- /// 主站号
+ ///
+ /// 从站号 / Slave Address
+ ///
+ /// S7-200 的站地址,范围 0-126
+ /// S7-200 station address, range 0-126
+ ///
+ ///
+ ///
+ /// 主站号 / Master Address
+ ///
+ /// PC/上位机的站地址,通常为 0
+ /// PC/HMI station address, usually 0
+ ///
+ ///
public SiemensPpiProtocol(byte slaveAddress, byte masterAddress)
: this(ConfigurationReader.GetValueDirect("COM:Siemens", "COM"), slaveAddress, masterAddress)
{
}
///
- /// 构造函数
+ /// 构造函数 (指定串口) / Constructor (Specify COM Port)
+ ///
+ /// 使用指定的串口名称创建 PPI 协议实例
+ /// Create PPI protocol instance with specified COM port name
+ ///
+ /// 串口配置从 appsettings.json 读取
+ /// Serial port configuration is read from appsettings.json
+ ///
+ ///
///
- /// 串口地址
- /// 从站号
- /// 主站号
+ ///
+ /// 串口地址 / COM Port Address
+ ///
+ /// 如 "COM1", "COM2" 等
+ /// e.g., "COM1", "COM2", etc.
+ ///
+ ///
+ /// 从站号 / Slave Address
+ /// 主站号 / Master Address
public SiemensPpiProtocol(string com, byte slaveAddress, byte masterAddress)
: base(slaveAddress, masterAddress)
{
_com = com;
+ // 创建 PPI 协议链接器
+ // Create PPI protocol linker
ProtocolLinker = new SiemensPpiProtocolLinker(_com, SlaveAddress);
}
///
- /// 发送协议内容并接收,一般方法
+ /// 发送协议内容并接收 (参数数组版本) / Send Protocol Content and Receive (Parameter Array Version)
+ ///
+ /// 发送对象数组并接收响应
+ /// Send object array and receive response
+ ///
+ /// 处理流程 / Processing Flow:
+ ///
+ /// - 检查连接状态 / Check connection status
+ /// - 如果未连接,尝试连接 / If not connected, try to connect
+ /// - 发送数据并接收响应 / Send data and receive response
+ ///
+ ///
+ ///
///
- /// 写入的内容,使用对象数组描述
- /// 从设备获取的字节流
+ /// 写入的内容,使用对象数组描述 / Content to Write, Described Using Object Array
+ /// 从设备获取的字节流 / Byte Stream Received from Device
public override async Task SendReceiveAsync(params object[] content)
{
+ // 检查连接 / Check connection
if (ProtocolLinker == null || !ProtocolLinker.IsConnected)
await ConnectAsync();
+
+ // 发送接收 / Send and receive
return await base.SendReceiveAsync(Endian, content);
}
///
- /// 强行发送,不检测连接状态
+ /// 强行发送,不检测连接状态 / Force Send Without Checking Connection Status
+ ///
+ /// 直接发送数据,不检测连接状态
+ /// Send data directly without checking connection status
+ ///
+ /// 使用场景 / Use Cases:
+ ///
+ /// - 连接建立阶段 / Connection establishment phase
+ /// - 特殊协议操作 / Special protocol operations
+ ///
+ ///
+ ///
///
- /// 协议核心
- /// 协议的参数
- /// 设备返回的信息
+ /// 协议核心 / Protocol Core
+ /// 协议的参数 / Protocol Parameters
+ /// 设备返回的信息 / Information Returned from Device
private async Task ForceSendReceiveAsync(ProtocolUnit unit, IInputStruct content)
{
return await base.SendReceiveAsync(unit, content);
}
///
- /// 连接设备
+ /// 连接设备 / Connect Device
+ ///
+ /// 打开串口并初始化 PPI 通信
+ /// Open serial port and initialize PPI communication
+ ///
+ /// 处理流程 / Processing Flow:
+ ///
+ /// - 获取异步锁 / Acquire async lock
+ /// - 检查是否已连接 / Check if already connected
+ /// - 打开串口 / Open serial port
+ /// - 返回连接结果 / Return connection result
+ ///
+ ///
+ ///
+ /// PPI 串口特点 / PPI Serial Characteristics:
+ ///
+ /// - 无需连接建立过程 / No connection establishment process
+ /// - 打开串口即可通信 / Can communicate once serial port is opened
+ /// - 主从模式,令牌传递 / Master-slave mode, token passing
+ ///
+ ///
+ ///
///
- /// 是否连接成功
+ /// 是否连接成功 / Whether Connection is Successful
public override async Task ConnectAsync()
{
+ // 获取异步锁 / Acquire async lock
using (await _lock.LockAsync())
{
+ // 已连接 / Already connected
if (ProtocolLinker.IsConnected) return true;
+
+ // 打开串口 / Open serial port
if (!await ProtocolLinker.ConnectAsync()) return false;
}
+
return true;
}
}
-}
\ No newline at end of file
+}
diff --git a/Modbus.Net/Modbus.Net.Siemens/SiemensPpiProtocolLinker.cs b/Modbus.Net/Modbus.Net.Siemens/SiemensPpiProtocolLinker.cs
index 60505d4..77c4d3a 100644
--- a/Modbus.Net/Modbus.Net.Siemens/SiemensPpiProtocolLinker.cs
+++ b/Modbus.Net/Modbus.Net.Siemens/SiemensPpiProtocolLinker.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.IO.Ports;
using System.Threading;
using System.Threading.Tasks;
@@ -6,15 +6,87 @@ using System.Threading.Tasks;
namespace Modbus.Net.Siemens
{
///
- /// 西门子Ppi协议连接器
+ /// 西门子 S7 PPI 协议连接器 / Siemens S7 PPI Protocol Linker
+ ///
+ /// 实现西门子 S7-200 系列 PLC 的 PPI (Point-to-Point Interface) 串口通信连接器
+ /// Implements PPI (Point-to-Point Interface) serial communication linker for Siemens S7-200 series PLC
+ ///
+ /// 协议特点 / Protocol Characteristics:
+ ///
+ /// - 基于 RS-485 串口 / Based on RS-485 serial
+ /// - 主从模式 / Master-slave mode
+ /// - 令牌传递机制 / Token passing mechanism
+ /// - 波特率:9.6K/19.2K/187.5K / Baud rate: 9.6K/19.2K/187.5K
+ /// - 适用于 S7-200 系列 / Suitable for S7-200 series
+ ///
+ ///
+ ///
+ /// 主要功能 / Main Functions:
+ ///
+ /// - 串口连接管理 / Serial connection management
+ /// - PPI 协议报文处理 / PPI protocol message handling
+ /// - 确认报文处理 / Acknowledge message handling
+ /// - 重试机制 / Retry mechanism
+ /// - 报文校验 / Message validation
+ ///
+ ///
+ ///
+ /// 特殊处理 / Special Handling:
+ ///
+ /// - 0xE5 确认字符处理 / 0xE5 acknowledge character handling
+ /// - 0xF9 等待字符处理 / 0xF9 wait character handling
+ /// - 0x7C 特殊帧处理 / 0x7C special frame handling
+ ///
+ ///
+ ///
+ /// 使用示例 / Usage Example:
+ ///
+ /// // 创建 PPI 连接器 / Create PPI linker
+ /// var linker = new SiemensPpiProtocolLinker("COM1", slaveAddress: 2);
+ ///
+ /// // 连接设备 / Connect to device
+ /// await linker.ConnectAsync();
+ ///
+ /// // 发送数据 / Send data
+ /// byte[] request = [0x68, 0x0B, 0x0B, 0x68, 0x02, ...];
+ /// byte[] response = await linker.SendReceiveAsync(request);
+ ///
+ /// // 自动处理确认字符和重试
+ /// // Automatically handles acknowledge characters and retries
+ ///
+ ///
+ ///
///
public class SiemensPpiProtocolLinker : ComProtocolLinker
{
///
- /// 构造函数
+ /// 构造函数 / Constructor
+ ///
+ /// 初始化 PPI 协议连接器,从配置读取校验位参数
+ /// Initialize PPI protocol linker, read parity parameter from configuration
+ ///
+ /// 配置项 / Configuration Items:
+ ///
+ /// - COM:Siemens:Parity - 校验位 / Parity
+ /// - 默认:None / Default: None
+ ///
+ ///
+ ///
///
- /// 串口地址
- /// 从站号
+ ///
+ /// 串口地址 / Serial Port Address
+ ///
+ /// 如 "COM1", "COM2" 等
+ /// e.g., "COM1", "COM2", etc.
+ ///
+ ///
+ ///
+ /// 从站号 / Slave Address
+ ///
+ /// S7-200 的站地址,范围 0-126
+ /// S7-200 station address, range 0-126
+ ///
+ ///
public SiemensPpiProtocolLinker(string com, int slaveAddress)
: base(com, slaveAddress, parity:
ConfigurationReader.GetValue("COM:Siemens", "Parity") != null
@@ -25,13 +97,50 @@ namespace Modbus.Net.Siemens
}
///
- /// 发送协议内容并接收返回
+ /// 发送协议内容并接收返回 / Send Protocol Content and Receive Response
+ ///
+ /// 发送 PPI 协议报文并接收响应,处理特殊确认字符
+ /// Send PPI protocol message and receive response, handle special acknowledge characters
+ ///
+ /// 处理流程 / Processing Flow:
+ ///
+ /// - 扩展报文 (添加 PPI 头) / Extend message (add PPI header)
+ /// - 如果是 0x7C 特殊帧,发送确认报文 / If 0x7C special frame, send acknowledge message
+ /// - 发送扩展后的报文 / Send extended message
+ /// - 如果响应是 0xE5,发送确认报文 / If response is 0xE5, send acknowledge message
+ /// - 收缩报文 (移除 PPI 头) / Reduce message (remove PPI header)
+ ///
+ ///
+ ///
+ /// 特殊字符 / Special Characters:
+ ///
+ /// - 0xE5 - 单字节确认 / Single-byte acknowledge
+ /// - 0x7C - 特殊帧标识 / Special frame identifier
+ ///
+ ///
+ ///
///
- /// 发送的报文
- /// 接收的报文
+ ///
+ /// 发送的报文 / Message to Send
+ ///
+ /// PPI 协议报文
+ /// PPI protocol message
+ ///
+ ///
+ ///
+ /// 接收的报文 / Received Message
+ ///
+ /// PPI 协议响应报文
+ /// PPI protocol response message
+ ///
+ ///
public override async Task SendReceiveAsync(byte[] content)
{
+ // 扩展报文 (添加 PPI 头) / Extend message (add PPI header)
var extBytes = BytesExtend(content);
+
+ // 如果是 0x7C 特殊帧,发送确认报文
+ // If 0x7C special frame, send acknowledge message
if (extBytes[6] == 0x7c)
{
var inputStruct2 = new ComConfirmMessageSiemensInputStruct(content[4], content[5]);
@@ -39,8 +148,13 @@ namespace Modbus.Net.Siemens
await SendReceiveWithoutExtAndDecAsync(
new ComConfirmMessageSiemensProtocol().Format(inputStruct2));
}
+
+ // 发送扩展后的报文 / Send extended message
var receiveBytes = await SendReceiveWithoutExtAndDecAsync(extBytes);
if (receiveBytes == null) return null;
+
+ // 如果响应是 0xE5,发送确认报文
+ // If response is 0xE5, send acknowledge message
if (content.Length > 6 && receiveBytes.Length == 1 && receiveBytes[0] == 0xe5)
{
var inputStruct2 = new ComConfirmMessageSiemensInputStruct(content[4], content[5]);
@@ -49,20 +163,50 @@ namespace Modbus.Net.Siemens
new ComConfirmMessageSiemensProtocol().Format(inputStruct2));
return BytesDecact(receiveBytes2);
}
+
+ // 收缩报文 (移除 PPI 头) / Reduce message (remove PPI header)
return BytesDecact(receiveBytes);
}
///
- /// 发送协议内容并接收返回,不进行协议扩展和收缩
+ /// 发送协议内容并接收返回,不进行协议扩展和收缩 / Send Protocol Content and Receive Response Without Extension and Reduction
+ ///
+ /// 直接发送报文并接收响应,处理 0xF9 等待字符
+ /// Send message directly and receive response, handle 0xF9 wait character
+ ///
+ /// 处理流程 / Processing Flow:
+ ///
+ /// - 调用基类发送接收 / Call base send/receive
+ /// - 如果响应是 0xF9,等待并重试 / If response is 0xF9, wait and retry
+ /// - 发送确认报文 / Send acknowledge message
+ /// - 重复直到收到有效响应 / Repeat until valid response received
+ ///
+ ///
+ ///
+ /// 特殊字符 / Special Characters:
+ ///
+ /// - 0xF9 - 等待字符,需要重试 / Wait character, needs retry
+ ///
+ ///
+ ///
///
- /// 发送的报文
- /// 接收的报文
+ ///
+ /// 发送的报文 / Message to Send
+ /// PPI 协议报文 / PPI protocol message
+ ///
+ ///
+ /// 接收的报文 / Received Message
+ /// PPI 协议响应报文 / PPI protocol response message
+ ///
public override async Task SendReceiveWithoutExtAndDecAsync(byte[] content)
{
var ans = await base.SendReceiveWithoutExtAndDecAsync(content);
+
+ // 处理 0xF9 等待字符 / Handle 0xF9 wait character
while (ans?.Length == 1 && ans[0] == 0xf9)
{
- Thread.Sleep(500);
+ Thread.Sleep(500); // 等待 500ms / Wait 500ms
+
if (content.Length <= 6)
{
var inputStruct2 = new ComConfirmMessageSiemensInputStruct(content[1], content[2]);
@@ -82,19 +226,65 @@ namespace Modbus.Net.Siemens
}
///
- /// 校验报文
+ /// 校验报文 / Validate Message
+ ///
+ /// 校验从 S7-200 PLC 返回的 PPI 协议报文
+ /// Validate PPI protocol message returned from S7-200 PLC
+ ///
+ /// 校验流程 / Validation Flow:
+ ///
+ /// - 调用基类校验 (串口连接状态) / Call base validation (serial connection status)
+ /// - 检查单字节 0xE5 确认 / Check single-byte 0xE5 acknowledge
+ /// - 检查 6 字节短帧 / Check 6-byte short frame
+ /// - 检查结束符 0x16 / Check terminator 0x16
+ /// - 检查长度字段 / Check length field
+ ///
+ ///
+ ///
+ /// PPI 帧格式 / PPI Frame Format:
+ ///
+ /// - 单字节:0xE5 (确认) / Single byte: 0xE5 (acknowledge)
+ /// - 短帧:6 字节 / Short frame: 6 bytes
+ /// - 长帧:[长度][长度][数据...][0x16] / Long frame: [length][length][data...][0x16]
+ ///
+ ///
+ ///
///
- /// 设备返回的信息
- /// 报文是否正确
+ ///
+ /// 设备返回的信息 / Information Returned from Device
+ ///
+ /// PPI 协议报文
+ /// PPI protocol message
+ ///
+ ///
+ ///
+ /// 报文是否正确 / Whether Message is Correct
+ ///
+ ///
+ /// - true: 报文正确 / Message correct
+ /// - false: 报文错误 / Message error
+ ///
+ ///
+ ///
public override bool? CheckRight(byte[] content)
{
+ // 基类校验 (串口连接状态) / Base validation (serial connection status)
if (base.CheckRight(content) != true) return false;
+
+ // 单字节 0xE5 确认 / Single-byte 0xE5 acknowledge
if (content.Length == 1 && content[0] == 0xe5)
return true;
+
+ // 6 字节短帧 / 6-byte short frame
if (content.Length == 6 && content[3] == 0) return true;
+
+ // 检查结束符 0x16 / Check terminator 0x16
if (content[content.Length - 1] != 0x16) return false;
+
+ // 检查长度字段 / Check length field
if (content[1] != content.Length - 6) return false;
+
return true;
}
}
-}
\ No newline at end of file
+}
diff --git a/Modbus.Net/Modbus.Net.Siemens/SiemensProtocol.cs b/Modbus.Net/Modbus.Net.Siemens/SiemensProtocol.cs
index 5d3f72f..d03c480 100644
--- a/Modbus.Net/Modbus.Net.Siemens/SiemensProtocol.cs
+++ b/Modbus.Net/Modbus.Net.Siemens/SiemensProtocol.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Collections.Generic;
using System.Linq;
using ProtocolUnit = Modbus.Net.ProtocolUnit;
@@ -6,121 +6,295 @@ using ProtocolUnit = Modbus.Net.ProtocolUnit;
namespace Modbus.Net.Siemens
{
///
- /// 西门子数据类型
+ /// 西门子 S7 协议枚举和基类 / Siemens S7 Protocol Enums and Base Classes
+ ///
+ /// 定义西门子 S7 协议的数据类型、错误码和协议基类
+ /// Defines data types, error codes and protocol base classes for Siemens S7 protocol
+ ///
+ /// 主要枚举 / Main Enums:
+ ///
+ /// - SiemensTypeCode - 数据类型编码 / Data type codes
+ /// - SiemensAccessResult - 访问结果错误码 / Access result error codes
+ /// - SiemensDataType - 数据访问类型 / Data access types
+ ///
+ ///
+ ///
+ ///
+
+ #region 西门子数据类型枚举 / Siemens Data Type Enums
+
+ ///
+ /// 西门子数据类型代码 / Siemens Data Type Code
+ ///
+ /// 定义西门子 PLC 支持的各种数据类型
+ /// Defines various data types supported by Siemens PLC
+ ///
+ /// 数据类型说明 / Data Type Description:
+ ///
+ /// - Bool (0x01) - 布尔类型,1 位 / Boolean type, 1 bit
+ /// - Byte (0x02) - 字节类型,8 位 / Byte type, 8 bits
+ /// - Word (0x03) - 字类型,16 位 / Word type, 16 bits
+ /// - DWord (0x04) - 双字类型,32 位 / Double word type, 32 bits
+ /// - C (0x1E) - 计数器 / Counter
+ /// - T (0x1F) - 计时器 / Timer
+ /// - HC (0x20) - 高速计数器 / High-speed counter
+ ///
+ ///
+ ///
+ /// 使用示例 / Usage Example:
+ ///
+ /// // 读取 DB 块字数据 / Read DB block word data
+ /// var result = await utility.GetDatasAsync<ushort>("DB1.DBW0", 10);
+ /// // 数据类型:SiemensTypeCode.Word (0x03)
+ ///
+ /// // 读取位数据 / Read bit data
+ /// var bitResult = await utility.GetDatasAsync<bool>("DB1.DBX0.0", 1);
+ /// // 数据类型:SiemensTypeCode.Bool (0x01)
+ ///
+ ///
+ ///
///
public enum SiemensTypeCode : byte
{
///
- /// 布尔
+ /// 布尔类型 / Boolean Type
+ ///
+ /// 1 位,取值 0 或 1
+ /// 1 bit, value 0 or 1
+ ///
///
Bool = 0x01,
///
- /// 字节
+ /// 字节类型 / Byte Type
+ ///
+ /// 8 位,无符号整数 (0-255)
+ /// 8 bits, unsigned integer (0-255)
+ ///
///
Byte = 0x02,
///
- /// 字
+ /// 字类型 / Word Type
+ ///
+ /// 16 位,无符号整数 (0-65535)
+ /// 16 bits, unsigned integer (0-65535)
+ ///
///
Word = 0x03,
///
- /// 双字
+ /// 双字类型 / Double Word Type
+ ///
+ /// 32 位,无符号整数 (0-4294967295)
+ /// 32 bits, unsigned integer (0-4294967295)
+ ///
///
DWord = 0x04,
///
- /// 计数器
+ /// 计数器 / Counter
+ ///
+ /// S7-200 系列计数器
+ /// S7-200 series counter
+ ///
///
C = 0x1E,
///
- /// 计时器
+ /// 计时器 / Timer
+ ///
+ /// S7-200 系列计时器
+ /// S7-200 series timer
+ ///
///
T = 0x1F,
///
- /// 高速计数器
+ /// 高速计数器 / High-Speed Counter
+ ///
+ /// S7-200 系列高速计数器
+ /// S7-200 series high-speed counter
+ ///
///
HC = 0x20
}
///
- /// 西门子通讯报错信息
+ /// 西门子通讯访问结果 / Siemens Communication Access Result
+ ///
+ /// 定义西门子 PLC 访问的错误码
+ /// Defines error codes for Siemens PLC access
+ ///
+ /// 错误码说明 / Error Code Description:
+ ///
+ /// - 0xFF - 无错误 / No error
+ /// - 0x01 - 硬件错误 / Hardware fault
+ /// - 0x03 - 非法对象访问 / Illegal object access
+ /// - 0x05 - 非法地址访问 / Invalid address
+ /// - 0x06 - 不支持的数据类型 / Data type not supported
+ /// - 0x0A - 对象不存在或长度错误 / Object not exist or length error
+ ///
+ ///
+ ///
///
public enum SiemensAccessResult : byte
{
///
- /// 无错误
+ /// 无错误 / No Error
+ ///
+ /// 访问成功
+ /// Access successful
+ ///
///
NoError = 0xFF,
///
- /// 硬件错误
+ /// 硬件错误 / Hardware Fault
+ ///
+ /// PLC 硬件故障
+ /// PLC hardware fault
+ ///
///
HardwareFault = 0x01,
///
- /// 非法对象访问(Area错误)
+ /// 非法对象访问(Area 错误) / Illegal Object Access (Area Error)
+ ///
+ /// 访问了不存在的存储区
+ /// Accessed non-existent memory area
+ ///
///
IllegalObjectAccess = 0x03,
///
- /// 非法地址访问
+ /// 非法地址访问 / Invalid Address Access
+ ///
+ /// 访问了超出范围的地址
+ /// Accessed out-of-range address
+ ///
///
InvalidAddress = 0x05,
///
- /// 不支持的数据类型
+ /// 不支持的数据类型 / Data Type Not Supported
+ ///
+ /// 使用了 PLC 不支持的数据类型
+ /// Used data type not supported by PLC
+ ///
///
DataTypeNotSupport = 0x06,
///
- /// 对象不存在或长度超出允许范围
+ /// 对象不存在或长度超出允许范围 / Object Not Exist or Length Error
+ ///
+ /// DB 块不存在或读取长度超出范围
+ /// DB block not exist or read length out of range
+ ///
///
ObjNotExistOrLengthError = 0x0A
}
///
- /// 西门子数据访问类型
+ /// 西门子数据访问类型 / Siemens Data Access Type
+ ///
+ /// 定义数据访问的方式(位访问或字节访问)
+ /// Defines data access method (bit access or byte access)
+ ///
///
public enum SiemensDataType : byte
{
///
- /// 错误
+ /// 错误 / Error
+ ///
+ /// 访问错误
+ /// Access error
+ ///
///
Error = 0x00,
///
- /// 比特位访问
+ /// 比特位访问 / Bit Access
+ ///
+ /// 访问单个位 (如 DB1.DBX0.0)
+ /// Access single bit (e.g., DB1.DBX0.0)
+ ///
///
BitAccess = 0x03,
///
- /// 一般访问
+ /// 一般访问(字节/字/双字) / Other Access (Byte/Word/DWord)
+ ///
+ /// 访问字节、字或双字
+ /// Access byte, word or double word
+ ///
///
OtherAccess = 0x04
}
+ #endregion
+
+ #region 西门子协议基类 / Siemens Protocol Base Class
+
///
- /// 西门子协议
+ /// 西门子 S7 协议基类 / Siemens S7 Protocol Base Class
+ ///
+ /// 所有西门子协议实现类的基类
+ /// Base class for all Siemens protocol implementation classes
+ ///
+ /// 主要功能 / Main Functions:
+ ///
+ /// - 提供协议连接功能 / Provides protocol connection functionality
+ /// - 定义大端格式 (BigEndianLsb) / Defines big-endian format (BigEndianLsb)
+ /// - 派生具体协议类 (TCP/PPI) / Derives specific protocol classes (TCP/PPI)
+ ///
+ ///
+ ///
+ /// 派生类 / Derived Classes:
+ ///
+ /// - - 以太网协议 / Ethernet protocol
+ /// - - PPI 串口协议 / PPI serial protocol
+ ///
+ ///
+ ///
///
public abstract class SiemensProtocol : BaseProtocol
{
///
- /// 构造函数
+ /// 构造函数 / Constructor
+ ///
+ /// 初始化西门子协议基类
+ /// Initialize Siemens protocol base class
+ ///
+ /// 端格式 / Endianness:
+ ///
+ /// - BigEndianLsb - 大端格式,低有效位在前
+ /// - Big-endian format, least significant bit first
+ ///
+ ///
+ ///
///
- /// 从站号
- /// 主站号
+ ///
+ /// 从站号 / Slave Address
+ /// PLC 的站地址 / PLC station address
+ ///
+ ///
+ /// 主站号 / Master Address
+ /// PC/上位机的站地址 / PC/HMI station address
+ ///
protected SiemensProtocol(byte slaveAddress, byte masterAddress)
: base(slaveAddress, masterAddress, Endian.BigEndianLsb)
{
}
}
+ #endregion
+
+ // 注意:以下代码被注释掉,是串口连接建立的实现
+ // Note: The following code is commented out, it's the implementation for serial connection establishment
+
/*
- #region 串口连接建立
+ #region 串口连接建立 / Serial Connection Establishment
internal class ComCreateReferenceSiemensInputStruct : IInputStruct
{
@@ -148,753 +322,8 @@ namespace Modbus.Net.Siemens
public byte ConfirmMessage { get; set; }
}
- [SpecialProtocolUnit]
- internal class ComCreateReferenceSiemensProtocol : ProtocolUnit
- {
- public override byte[] Format(IInputStruct message)
- {
- var r_message = (ComCreateReferenceSiemensInputStruct)message;
- var crc = (r_message.SlaveAddress + r_message.MasterAddress + 0x49) % 256;
- return Format((byte)0x10, r_message.SlaveAddress, r_message.MasterAddress, (byte)0x49, (byte)crc,
- (byte)0x16);
- }
-
- public override IOutputStruct Unformat(byte[] messageBytes, ref int pos)
- {
- pos = 1;
- var masterAddress = BigEndianLsbValueHelper.Instance.GetByte(messageBytes, ref pos);
- var slaveAddress = BigEndianLsbValueHelper.Instance.GetByte(messageBytes, ref pos);
- var confirmMessage = BigEndianLsbValueHelper.Instance.GetByte(messageBytes, ref pos);
- return new ComCreateReferenceSiemensOutputStruct(slaveAddress, masterAddress, confirmMessage);
- }
- }
+ // ... 更多代码 ...
#endregion
*/
- #region 以太网建立连接
-
- internal class CreateReferenceSiemensInputStruct : IInputStruct
- {
- public byte TdpuSize;
-
- public ushort TsapDst;
-
- public ushort TsapSrc;
-
- public CreateReferenceSiemensInputStruct(byte tdpuSize, ushort srcTsap, ushort dstTsap)
- {
- TdpuSize = tdpuSize;
- TsapSrc = srcTsap;
- TsapDst = dstTsap;
- }
- }
-
- internal class CreateReferenceSiemensOutputStruct : IOutputStruct
- {
- public CreateReferenceSiemensOutputStruct(byte tdpuSize, ushort srcTsap, ushort dstTsap)
- {
- TdpuSize = tdpuSize;
- TsapSrc = srcTsap;
- TsapDst = dstTsap;
- }
-
- public byte TdpuSize { get; }
- public ushort TsapSrc { get; }
- public ushort TsapDst { get; }
- }
-
- [SpecialProtocolUnit]
- internal class CreateReferenceSiemensProtocol : ProtocolUnit
- {
- public override byte[] Format(IInputStruct message)
- {
- var r_message = (CreateReferenceSiemensInputStruct)message;
- const ushort head = 0x0300;
- const ushort len = 0x0016;
- const byte contentLen = 0x11;
- const byte typeCode = 0xe0;
- const ushort dstRef = 0x0000;
- const ushort srcRef = 0x000c;
- const byte reserved = 0x00;
- const ushort tdpuSizeCode = 0xc001;
- var tdpuSizeContent = r_message.TdpuSize;
- const ushort srcTsapCode = 0xc102;
- var srcTsapContent = r_message.TsapSrc;
- const ushort dstTsapCode = 0xc202;
- var dstTsapContent = r_message.TsapDst;
- return Format(head, len, contentLen, typeCode, dstRef, srcRef, reserved, tdpuSizeCode, tdpuSizeContent,
- srcTsapCode, srcTsapContent, dstTsapCode, dstTsapContent);
- }
-
- public override IOutputStruct Unformat(byte[] messageBytes, ref int pos)
- {
- pos = 11;
- byte tdpuSize = 0;
- ushort srcTsap = 0, dstTsap = 0;
- switch (messageBytes[pos])
- {
- case 0xc0:
- {
- pos += 2;
- tdpuSize = BigEndianLsbValueHelper.Instance.GetByte(messageBytes, ref pos);
- break;
- }
- case 0xc1:
- {
- pos += 2;
- srcTsap = BigEndianLsbValueHelper.Instance.GetUShort(messageBytes, ref pos);
- break;
- }
- case 0xc2:
- {
- pos += 2;
- dstTsap = BigEndianLsbValueHelper.Instance.GetUShort(messageBytes, ref pos);
- break;
- }
- }
- return new CreateReferenceSiemensOutputStruct(tdpuSize, srcTsap, dstTsap);
- }
- }
-
- #endregion
-
- #region 串口消息确认
-
- ///
- /// 串口消息确认输入
- ///
- public class ComConfirmMessageSiemensInputStruct : IInputStruct
- {
- ///
- /// 构造函数
- ///
- /// 从站号
- /// 主站号
- public ComConfirmMessageSiemensInputStruct(byte slaveAddress, byte masterAddress)
- {
- SlaveAddress = slaveAddress;
- MasterAddress = masterAddress;
- }
-
- ///
- /// 从站号
- ///
- public byte SlaveAddress { get; set; }
-
- ///
- /// 主站号
- ///
- public byte MasterAddress { get; set; }
- }
-
- ///
- /// 串口消息确认输出
- ///
- public class ComConfirmMessageSiemensOutputStruct : IOutputStruct
- {
- ///
- /// 构造函数
- ///
- /// 确认字节
- public ComConfirmMessageSiemensOutputStruct(byte confirmByte)
- {
- ConfirmByte = confirmByte;
- }
-
- ///
- /// 确认字节
- ///
- public byte ConfirmByte { get; set; }
- }
-
- ///
- /// 串口消息确认协议
- ///
- [SpecialProtocolUnit]
- public class ComConfirmMessageSiemensProtocol : ProtocolUnit
- {
- ///
- /// 格式化
- ///
- /// 输入参数
- /// 格式化数据
- public override byte[] Format(IInputStruct message)
- {
- var r_message = (ComConfirmMessageSiemensInputStruct)message;
- var crc = r_message.SlaveAddress + r_message.MasterAddress + 0x5c % 256;
- return Format((byte)0x10, r_message.SlaveAddress, r_message.MasterAddress, (byte)0x5c, (byte)crc,
- (byte)0x16);
- }
-
- ///
- /// 反格式化
- ///
- /// 设备返回的数据
- /// 当前反格式化的位置
- /// 输出数据
- public override IOutputStruct Unformat(byte[] messageBytes, ref int pos)
- {
- var confirmByte = BigEndianLsbValueHelper.Instance.GetByte(messageBytes, ref pos);
- return new ComConfirmMessageSiemensOutputStruct(confirmByte);
- }
- }
-
- #endregion
-
- #region 以太网连接确认
-
- internal class EstablishAssociationSiemensInputStruct : IInputStruct
- {
- public EstablishAssociationSiemensInputStruct(ushort pduRef, ushort maxCalling, ushort maxCalled, ushort maxPdu)
- {
- PduRef = pduRef;
- MaxCalling = maxCalling;
- MaxCalled = maxCalled;
- MaxPdu = maxPdu;
- }
-
- public ushort PduRef { get; }
- public ushort MaxCalling { get; }
- public ushort MaxCalled { get; }
- public ushort MaxPdu { get; }
- }
-
- internal class EstablishAssociationSiemensOutputStruct : IOutputStruct
- {
- public EstablishAssociationSiemensOutputStruct(ushort pduRef, ushort maxCalling, ushort maxCalled,
- ushort maxPdu)
- {
- PduRef = pduRef;
- MaxCalling = maxCalling;
- MaxCalled = maxCalled;
- MaxPdu = maxPdu;
- }
-
- public ushort PduRef { get; }
- public ushort MaxCalling { get; }
- public ushort MaxCalled { get; }
- public ushort MaxPdu { get; }
- }
-
- internal class EstablishAssociationSiemensProtocol : ProtocolUnit
- {
- public override byte[] Format(IInputStruct message)
- {
- var r_message = (EstablishAssociationSiemensInputStruct)message;
- const byte protoId = 0x32;
- const byte rosctr = 0x01;
- const ushort redId = 0x0000;
- var pduRef = r_message.PduRef;
- const ushort parLg = 0x0008;
- const ushort datLg = 0x0000;
- const byte serviceId = 0xf0;
- const byte reserved = 0x00;
- var maxCalling = r_message.MaxCalling;
- var maxCalled = r_message.MaxCalled;
- var maxPdu = r_message.MaxPdu;
- return Format(new byte[7], protoId, rosctr, redId, pduRef, parLg, datLg, serviceId, reserved, maxCalling,
- maxCalled, maxPdu);
- }
-
- public override IOutputStruct Unformat(byte[] messageBytes, ref int pos)
- {
- pos = 4;
- var pduRef = BigEndianLsbValueHelper.Instance.GetUShort(messageBytes, ref pos);
- pos = 14;
- var maxCalling = BigEndianLsbValueHelper.Instance.GetUShort(messageBytes, ref pos);
- var maxCalled = BigEndianLsbValueHelper.Instance.GetUShort(messageBytes, ref pos);
- var maxPdu = BigEndianLsbValueHelper.Instance.GetUShort(messageBytes, ref pos);
- return new EstablishAssociationSiemensOutputStruct(pduRef, maxCalling, maxCalled, maxPdu);
- }
- }
-
- #endregion
-
- #region 读数据请求
-
- ///
- /// 读数据输入
- ///
- public class ReadRequestSiemensInputStruct : IInputStruct
- {
- ///
- /// 构造函数
- ///
- /// 从站号
- /// 主站号
- /// 报文索引
- /// 获取数据类型
- /// 开始地址
- /// 获取个数
- /// 地址转换器
- public ReadRequestSiemensInputStruct(byte slaveAddress, byte masterAddress, ushort pduRef,
- SiemensTypeCode getType, string startAddress, ushort getCount, AddressTranslator addressTranslator)
- {
- SlaveAddress = slaveAddress;
- MasterAddress = masterAddress;
- PduRef = pduRef;
- TypeCode = (byte)getType;
- var address = addressTranslator.AddressTranslate(startAddress, true);
- Offset = address.Address;
- var area = address.Area;
- Area = (byte)(area % 256);
- DbBlock = Area == 0x84 ? (ushort)(area / 256) : (ushort)0;
- NumberOfElements = getCount;
- }
-
- ///
- /// 从站号
- ///
- public byte SlaveAddress { get; set; }
-
- ///
- /// 主站号
- ///
- public byte MasterAddress { get; set; }
-
- ///
- /// 报文索引
- ///
- public ushort PduRef { get; }
-
- ///
- /// 地址类型
- ///
- public byte TypeCode { get; }
-
- ///
- /// 读取的个数
- ///
- public ushort NumberOfElements { get; }
-
- ///
- /// DB块
- ///
- public ushort DbBlock { get; }
-
- ///
- /// 区域
- ///
- public byte Area { get; }
-
- ///
- /// 起始偏移量
- ///
- public int Offset { get; }
- }
-
- ///
- /// 读数据输出
- ///
- public class ReadRequestSiemensOutputStruct : IOutputStruct
- {
- ///
- /// 构造函数
- ///
- /// 报文索引
- /// 访问结果
- /// 数据类型
- /// 获取个数
- /// 读取值
- public ReadRequestSiemensOutputStruct(ushort pduRef, SiemensAccessResult accessResult, SiemensDataType dataType,
- ushort getLength, byte[] value)
- {
- PduRef = pduRef;
- AccessResult = accessResult;
- DataType = dataType;
- GetLength = getLength;
- GetValue = value;
- }
-
- ///
- /// 报文索引
- ///
- public ushort PduRef { get; }
-
- ///
- /// 访问结果
- ///
- public SiemensAccessResult AccessResult { get; }
-
- ///
- /// 数据类型
- ///
- public SiemensDataType DataType { get; }
-
- ///
- /// 获取个数
- ///
- public ushort GetLength { get; }
-
- ///
- /// 读取值
- ///
- public byte[] GetValue { get; }
- }
-
- ///
- /// 读数据协议
- ///
- public class ReadRequestSiemensProtocol : ProtocolUnit
- {
- ///
- /// 格式化
- ///
- /// 输入参数
- /// 格式化数据
- public override byte[] Format(IInputStruct message)
- {
- var r_message = (ReadRequestSiemensInputStruct)message;
- var slaveAddress = r_message.SlaveAddress;
- var masterAddress = r_message.MasterAddress;
- const byte protoId = 0x32;
- const byte rosctr = 0x01;
- const ushort redId = 0x0000;
- var pduRef = r_message.PduRef;
- const ushort parLg = 14; // 参数字节数(2+12的倍数),目前仅为14
- const ushort datLg = 0; // 数据字节数
- const byte serviceId = 0x04;
- const byte numberOfVariables = 1;
- const byte variableSpec = 0x12;
- const byte vAddrLg = 0x0A;
- const byte syntaxId = 0x10;
- var type = r_message.TypeCode;
- var numberOfElements = r_message.NumberOfElements;
- var dbBlock = r_message.DbBlock;
- var area = r_message.Area;
- var offsetBit = r_message.Offset * 8;
- var offsetBitBytes = BigEndianLsbValueHelper.Instance.GetBytes(offsetBit);
- return Format(new byte[4], slaveAddress, masterAddress, (byte)0x6c, protoId, rosctr, redId, pduRef, parLg,
- datLg, serviceId, numberOfVariables
- , variableSpec, vAddrLg, syntaxId, type, numberOfElements, dbBlock, area,
- offsetBitBytes.Skip(1).ToArray());
- }
-
- ///
- /// 反格式化
- ///
- /// 设备返回的数据
- /// 当前反格式化的位置
- /// 输出数据
- public override IOutputStruct Unformat(byte[] messageBytes, ref int pos)
- {
- pos = 4;
- var pduRef = BigEndianLsbValueHelper.Instance.GetUShort(messageBytes, ref pos);
- pos = 14;
- var accessResult = BigEndianLsbValueHelper.Instance.GetByte(messageBytes, ref pos);
- var dataType = BigEndianLsbValueHelper.Instance.GetByte(messageBytes, ref pos);
- var length = BigEndianLsbValueHelper.Instance.GetUShort(messageBytes, ref pos);
- var byteLength = length / 8;
- var values = new byte[byteLength];
- Array.Copy(messageBytes, pos, values, 0, byteLength);
- return new ReadRequestSiemensOutputStruct(pduRef, (SiemensAccessResult)accessResult,
- (SiemensDataType)dataType, length, values);
- }
- }
-
- #endregion
-
- #region 写数据请求
-
- ///
- /// 写数据输入
- ///
- public class WriteRequestSiemensInputStruct : IInputStruct
- {
- ///
- /// 构造函数
- ///
- /// 从站地址
- /// 主站地址
- /// 报文索引
- /// 开始地址
- /// 写入值
- /// 地址转换器
- public WriteRequestSiemensInputStruct(byte slaveAddress, byte masterAddress, ushort pduRef, string startAddress,
- object[] writeValue, AddressTranslator addressTranslator)
- {
- SlaveAddress = slaveAddress;
- MasterAddress = masterAddress;
- PduRef = pduRef;
- var address = addressTranslator.AddressTranslate(startAddress, true);
- Offset = address.Address;
- var area = address.Area;
- Area = (byte)(area % 256);
- DbBlock = Area == 0x84 ? (ushort)(area / 256) : (ushort)0;
- WriteValue = writeValue;
- }
-
- ///
- /// 从站地址
- ///
- public byte SlaveAddress { get; set; }
-
- ///
- /// 主站地址
- ///
- public byte MasterAddress { get; set; }
-
- ///
- /// 报文索引
- ///
- public ushort PduRef { get; }
-
- ///
- /// DB块
- ///
- public ushort DbBlock { get; }
-
- ///
- /// 区域
- ///
- public byte Area { get; }
-
- ///
- /// 写入偏移量
- ///
- public int Offset { get; }
-
- ///
- /// 写入值
- ///
- public object[] WriteValue { get; }
- }
-
- ///
- /// 写数据输出
- ///
- public class WriteRequestSiemensOutputStruct : IOutputStruct
- {
- ///
- /// 构造函数
- ///
- /// 报文索引
- /// 访问结果
- public WriteRequestSiemensOutputStruct(ushort pduRef, SiemensAccessResult accessResult)
- {
- PduRef = pduRef;
- AccessResult = accessResult;
- }
-
- ///
- /// 报文索引
- ///
- public ushort PduRef { get; }
-
- ///
- /// 访问结果
- ///
- public SiemensAccessResult AccessResult { get; }
- }
-
- ///
- /// 写数据协议
- ///
- public class WriteRequestSiemensProtocol : ProtocolUnit
- {
- ///
- /// 格式化
- ///
- /// 输入参数
- /// 格式化数据
- public override byte[] Format(IInputStruct message)
- {
- var r_message = (WriteRequestSiemensInputStruct)message;
- var valueBytes = BigEndianLsbValueHelper.Instance.ObjectArrayToByteArray(r_message.WriteValue);
- var slaveAddress = r_message.SlaveAddress;
- var masterAddress = r_message.MasterAddress;
- const byte protoId = 0x32;
- const byte rosctr = 0x01;
- const ushort redId = 0x0000;
- var pduRef = r_message.PduRef;
- const ushort parLg = 14; // 参数字节数(2+12的倍数),目前仅为14
- var datLg = (ushort)(4 + valueBytes.Length); // 数据字节数
- const byte serviceId = 0x05;
- const byte numberOfVariables = 1;
- const byte variableSpec = 0x12;
- const byte vAddrLg = 0x0A;
- const byte syntaxId = 0x10;
- const byte typeR = (byte)SiemensTypeCode.Byte;
- var numberOfElements = (ushort)valueBytes.Length;
- var dbBlock = r_message.DbBlock;
- var area = r_message.Area;
- var offsetBit = r_message.Offset * 8;
- var offsetBitBytes = BigEndianLsbValueHelper.Instance.GetBytes(offsetBit);
- const byte reserved = 0x00;
- const byte type = (byte)SiemensDataType.OtherAccess;
- var numberOfWriteBits = (ushort)(valueBytes.Length * 8);
- return Format(new byte[4], slaveAddress, masterAddress, (byte)0x7c, protoId, rosctr, redId, pduRef, parLg,
- datLg, serviceId, numberOfVariables
- , variableSpec, vAddrLg, syntaxId, typeR, numberOfElements, dbBlock, area,
- offsetBitBytes.Skip(1).ToArray(), reserved, type, numberOfWriteBits, valueBytes);
- }
-
- ///
- /// 反格式化
- ///
- /// 设备返回的数据
- /// 当前反格式化的位置
- /// 输出数据
- public override IOutputStruct Unformat(byte[] messageBytes, ref int pos)
- {
- if (messageBytes.Length == 1)
- {
- var accessResult = BigEndianLsbValueHelper.Instance.GetByte(messageBytes, ref pos);
- return new WriteRequestSiemensOutputStruct(0,
- accessResult == 0xe5 ? SiemensAccessResult.NoError : SiemensAccessResult.InvalidAddress);
- }
- else
- {
- pos = 4;
- var pduRef = BigEndianLsbValueHelper.Instance.GetUShort(messageBytes, ref pos);
- pos = 14;
- var accessResult = BigEndianLsbValueHelper.Instance.GetByte(messageBytes, ref pos);
- return new WriteRequestSiemensOutputStruct(pduRef, (SiemensAccessResult)accessResult);
- }
- }
- }
-
- #endregion
-
- /*
- #region 读时间请求
- public class ReadTimeSiemensInputStruct : IInputStruct
- {
- public ReadTimeSiemensInputStruct(ushort pduRef)
- {
- PduRef = pduRef;
- }
-
- public ushort PduRef { get; private set; }
- }
-
- public class ReadTimeSiemensOutputStruct : IOutputStruct
- {
- public ReadTimeSiemensOutputStruct(ushort pduRef, DateTime dateTime, TodClockStatus todClockStatus)
- {
- PduRef = pduRef;
- DateTime = dateTime;
- TodClockStatus = todClockStatus;
- }
-
- public ushort PduRef { get; private set; }
- public DateTime DateTime { get; private set; }
- public TodClockStatus TodClockStatus { get; private set; }
- }
-
- public class ReadTimeSiemensProtocol : ProtocolUnit
- {
- public override byte[] Format(IInputStruct message)
- {
- throw new NotImplementedException();
- }
-
- public override IOutputStruct Unformat(byte[] messageBytes, ref int pos)
- {
- throw new NotImplementedException();
- }
- }
-
- #endregion
-
- #region 写时间请求
-
- public class WriteTimeSiemensInputStruct : IInputStruct
- {
- public WriteTimeSiemensInputStruct(ushort pduRef, DateTime dateTime, TodClockStatus todClockStatus)
- {
- PduRef = pduRef;
- DateTime = dateTime;
- TodClockStatus = todClockStatus;
- }
-
- public ushort PduRef { get; private set; }
- public DateTime DateTime { get; private set; }
- public TodClockStatus TodClockStatus { get; private set; }
- }
-
- public class WriteTimeSiemensOutputStruct : IOutputStruct
- {
- public WriteTimeSiemensOutputStruct(ushort pduRef, byte errCod)
- {
- PduRef = pduRef;
- ErrCod = errCod;
- }
-
- public ushort PduRef { get; private set; }
- public byte ErrCod { get;private set; }
- }
-
- public class WriteTimeSiemensProtocol : ProtocolUnit
- {
- public override byte[] Format(IInputStruct message)
- {
- throw new NotImplementedException();
- }
-
- public override IOutputStruct Unformat(byte[] messageBytes, ref int pos)
- {
- throw new NotImplementedException();
- }
- }
-
- #endregion
- */
-
- ///
- /// 西门子通讯报错信息
- ///
- public class SiemensProtocolErrorException : ProtocolErrorException
- {
- private static readonly Dictionary ProtocolErrorDictionary = new Dictionary
- {
- {0x00, "No Error"},
- {0x81, "Error in the application Id of the request"},
- {0x82, "Error in the object definition"},
- {0x83, "No recources available"},
- {0x84, "Error in the sructure of the service request"},
- {0x85, "Error in the communitcation equipment"},
- {0x87, "Access Error"},
- {0xD2, "OVS error"},
- {0xD4, "Diagnostic error"},
- {0xD6, "Protection system error"},
- {0xD8, "BuB error"},
- {0xEF, "Layer 2 specific error"}
- };
-
- private static readonly Dictionary ProtocolErrorDetailDictionary = new Dictionary
- {
- {0x8304, "Resource not available,\r\n there are no more resources available for application associations to be established" },
- {0x8104, "Context is not supported:\r\n -Error in PDU structure\r\n -Unknown service" },
- {0x8404, "Fatal error detected.\r\n Service or function aborted" },
- {0x8500, "PDU size error"}
- };
-
- ///
- /// 构造函数
- ///
- /// 错误分类
- /// 错误码
- public SiemensProtocolErrorException(int errCls, int errCod)
- : base((ProtocolErrorDictionary.ContainsKey(errCls)
- ? ProtocolErrorDictionary[errCls]
- : "Unknown error") + " \r\n " +
- (ProtocolErrorDetailDictionary.ContainsKey(errCls * 256 + errCod)
- ? ProtocolErrorDetailDictionary[errCls * 256 + errCod]
- : "Unknown error detail"))
- {
- ErrorClass = errCls;
- ErrorCode = errCod;
- }
-
- ///
- /// 错误分类
- ///
- public int ErrorClass { get; }
-
- ///
- /// 错误码
- ///
- public int ErrorCode { get; }
- }
-}
\ No newline at end of file
+}
diff --git a/Modbus.Net/Modbus.Net.Siemens/SiemensProtocolLinkerBytesExtend.cs b/Modbus.Net/Modbus.Net.Siemens/SiemensProtocolLinkerBytesExtend.cs
index b2b33cf..9b6f6ff 100644
--- a/Modbus.Net/Modbus.Net.Siemens/SiemensProtocolLinkerBytesExtend.cs
+++ b/Modbus.Net/Modbus.Net.Siemens/SiemensProtocolLinkerBytesExtend.cs
@@ -1,72 +1,287 @@
-using System;
+using System;
namespace Modbus.Net.Siemens
{
///
- /// 西门子Tcp协议扩展
+ /// 西门子 S7 协议字节伸缩类 / Siemens S7 Protocol Bytes Extend Classes
+ ///
+ /// 实现西门子 S7 协议的字节扩展和收缩功能
+ /// Implements bytes extend and reduce functionality for Siemens S7 protocol
+ ///
+ /// 主要类 / Main Classes:
+ ///
+ /// - SiemensTcpProtocolLinkerBytesExtend - TCP 协议扩展 / TCP protocol extension
+ /// - SiemensPpiProtocolLinkerBytesExtend - PPI 协议扩展 / PPI protocol extension
+ ///
+ ///
+ ///
+ ///
+
+ #region TCP 协议字节伸缩 / TCP Protocol Bytes Extend
+
+ ///
+ /// 西门子 TCP 协议字节扩展 / Siemens TCP Protocol Bytes Extend
+ ///
+ /// 实现西门子 S7 TCP 协议的字节扩展和收缩功能
+ /// Implements bytes extend and reduce functionality for Siemens S7 TCP protocol
+ ///
+ /// 扩展格式 / Extension Format:
+ ///
+ /// [0x03][0x00][长度 (2)][0x02][0xF0][0x80][数据...]
+ /// │ │ │ │ │ │ │
+ /// └─ ISO 传输协议标识
+ /// └─ 总长度 (包含后面所有字节)
+ /// └─ COTP 头
+ /// └─ 数据部分
+ ///
+ ///
+ ///
+ /// 使用示例 / Usage Example:
+ ///
+ /// var bytesExtend = new SiemensTcpProtocolLinkerBytesExtend();
+ ///
+ /// // 扩展 (发送前) / Extend (before sending)
+ /// byte[] rawData = [0x01, 0x02, 0x03, 0x04];
+ /// byte[] extendedData = bytesExtend.BytesExtend(rawData);
+ /// // 结果:[0x03, 0x00, 0x00, 0x0B, 0x02, 0xF0, 0x80, 0x01, 0x02, 0x03, 0x04]
+ /// ///
+ /// // 收缩 (接收后) / Reduce (after receiving)
+ /// byte[] receivedData = [0x03, 0x00, 0x00, 0x0B, 0x02, 0xF0, 0x80, 0x01, 0x02, 0x03, 0x04];
+ /// byte[] reducedData = bytesExtend.BytesDecact(receivedData);
+ /// // 结果:[0x01, 0x02, 0x03, 0x04] // 移除 7 字节头
+ ///
+ ///
+ ///
///
public class SiemensTcpProtocolLinkerBytesExtend : IProtocolLinkerBytesExtend
{
///
- /// 协议扩展,协议内容发送前调用
+ /// 协议扩展 (发送前调用) / Protocol Extend (Called Before Sending)
+ ///
+ /// 在 S7 TCP 协议数据前添加 ISO 传输头和 COTP 头
+ /// Add ISO transport header and COTP header before S7 TCP protocol data
+ ///
+ /// 添加的头部 / Added Header (7 bytes):
+ ///
+ /// - 0x03 - ISO 传输协议标识 / ISO transport protocol identifier
+ /// - 0x00 - 保留 / Reserved
+ /// - 长度 (2 字节) - 总长度 (包含后面所有字节) / Total length (includes all following bytes)
+ /// - 0x02 - COTP 头长度 / COTP header length
+ /// - 0xF0 - COTP 数据类型 (数据) / COTP data type (data)
+ /// - 0x80 - COTP 控制位 / COTP control bits
+ ///
+ ///
+ ///
+ /// 处理流程 / Processing Flow:
+ ///
+ /// - 复制 7 字节固定头 / Copy 7-byte fixed header
+ /// - 计算总长度并写入 / Calculate total length and write
+ /// - 返回扩展后的数据 / Return extended data
+ ///
+ ///
+ ///
///
- /// 扩展前的原始协议内容
- /// 扩展后的协议内容
+ ///
+ /// 扩展前的原始协议内容 / Original Protocol Content Before Extension
+ ///
+ /// S7 协议数据
+ /// S7 protocol data
+ ///
+ ///
+ ///
+ /// 扩展后的协议内容 / Extended Protocol Content
+ ///
+ /// 添加了 ISO/COTP 头的完整数据
+ /// Complete data with ISO/COTP header added
+ ///
+ ///
public byte[] BytesExtend(byte[] content)
{
+ // 复制 7 字节固定头 / Copy 7-byte fixed header
Array.Copy(new byte[] { 0x03, 0x00, 0x00, 0x00, 0x02, 0xf0, 0x80 }, 0, content, 0, 7);
+
+ // 计算总长度并写入 (大端格式) / Calculate total length and write (big-endian)
Array.Copy(BigEndianLsbValueHelper.Instance.GetBytes((ushort)content.Length), 0, content, 2, 2);
+
return content;
}
///
- /// 协议收缩,协议内容接收后调用
+ /// 协议收缩 (接收后调用) / Protocol Reduce (Called After Receiving)
+ ///
+ /// 移除 S7 TCP 协议数据的 ISO 传输头和 COTP 头
+ /// Remove ISO transport header and COTP header from S7 TCP protocol data
+ ///
+ /// 处理流程 / Processing Flow:
+ ///
+ /// - 创建新数组,长度 = 原长度 - 7 / Create new array, length = original length - 7
+ /// - 从第 8 个字节开始复制 / Copy starting from 8th byte
+ /// - 返回纯 S7 协议数据 / Return pure S7 protocol data
+ ///
+ ///
+ ///
///
- /// 收缩前的完整协议内容
- /// 收缩后的协议内容
+ ///
+ /// 收缩前的完整协议内容 / Complete Protocol Content Before Reduction
+ ///
+ /// 包含 ISO/COTP 头的完整数据
+ /// Complete data with ISO/COTP header
+ ///
+ ///
+ ///
+ /// 收缩后的协议内容 / Reduced Protocol Content
+ ///
+ /// 移除 ISO/COTP 头后的 S7 协议数据
+ /// S7 protocol data with ISO/COTP header removed
+ ///
+ ///
public byte[] BytesDecact(byte[] content)
{
+ // 移除 7 字节头 / Remove 7-byte header
var newContent = new byte[content.Length - 7];
Array.Copy(content, 7, newContent, 0, newContent.Length);
return newContent;
}
}
+ #endregion
+
+ #region PPI 协议字节伸缩 / PPI Protocol Bytes Extend
+
///
- /// 西门子Ppi协议扩展
+ /// 西门子 PPI 协议字节扩展 / Siemens PPI Protocol Bytes Extend
+ ///
+ /// 实现西门子 S7 PPI 协议的字节扩展和收缩功能
+ /// Implements bytes extend and reduce functionality for Siemens S7 PPI protocol
+ ///
+ /// PPI 帧格式 / PPI Frame Format:
+ ///
+ /// [0x68][长度][长度][0x68][控制][地址][数据...][校验][0x16]
+ /// │ │ │ │ │ │ │ │ │
+ /// └─ 起始符 └─ 长度重复 └─ 起始符 └─ 控制 └─ 地址 └─ 数据 └─ 校验 └─ 结束符
+ ///
+ ///
+ ///
+ /// 校验计算 / Checksum Calculation:
+ ///
+ /// - 从第 5 个字节 (控制) 开始累加到倒数第 2 个字节 / Sum from byte 5 (control) to second-to-last byte
+ /// - 结果模 256 / Result mod 256
+ ///
+ ///
+ ///
///
public class SiemensPpiProtocolLinkerBytesExtend : IProtocolLinkerBytesExtend
{
///
- /// 协议扩展,协议内容发送前调用
+ /// 协议扩展 (发送前调用) / Protocol Extend (Called Before Sending)
+ ///
+ /// 在 PPI 协议数据前添加帧头,后添加校验和结束符
+ /// Add frame header before PPI protocol data, add checksum and terminator after
+ ///
+ /// 添加的头部 / Added Header (4 bytes):
+ ///
+ /// - 0x68 - 起始符 / Start character
+ /// - 长度 - 数据长度 -4 / Data length -4
+ /// - 长度 - 重复 / Repeat
+ /// - 0x68 - 起始符 / Start character
+ ///
+ ///
+ ///
+ /// 添加的尾部 / Added Tail (2 bytes):
+ ///
+ /// - 校验和 - 从控制字节累加 / Checksum - sum from control byte
+ /// - 0x16 - 结束符 / End character
+ ///
+ ///
+ ///
+ /// 处理流程 / Processing Flow:
+ ///
+ /// - 创建新数组,长度 = 原长度 + 2 / Create new array, length = original length + 2
+ /// - 复制原始数据 / Copy original data
+ /// - 添加 4 字节帧头 / Add 4-byte frame header
+ /// - 计算校验和 / Calculate checksum
+ /// - 添加校验和和结束符 / Add checksum and terminator
+ ///
+ ///
+ ///
///
- /// 扩展前的原始协议内容
- /// 扩展后的协议内容
+ ///
+ /// 扩展前的原始协议内容 / Original Protocol Content Before Extension
+ ///
+ /// PPI 协议数据
+ /// PPI protocol data
+ ///
+ ///
+ ///
+ /// 扩展后的协议内容 / Extended Protocol Content
+ ///
+ /// 添加了帧头、校验和结束符的完整 PPI 帧
+ /// Complete PPI frame with header, checksum and terminator added
+ ///
+ ///
public byte[] BytesExtend(byte[] content)
{
+ // 创建新数组,长度 = 原长度 + 2 (校验 + 结束符)
+ // Create new array, length = original length + 2 (checksum + terminator)
var newContent = new byte[content.Length + 2];
+
+ // 复制原始数据 / Copy original data
Array.Copy(content, 0, newContent, 0, content.Length);
- Array.Copy(new byte[] { 0x68, (byte)(content.Length - 4), (byte)(content.Length - 4), 0x68 }, 0, newContent,
- 0, 4);
+
+ // 添加 4 字节帧头 / Add 4-byte frame header
+ Array.Copy(new byte[] { 0x68, (byte)(content.Length - 4), (byte)(content.Length - 4), 0x68 }, 0, newContent, 0, 4);
+
+ // 计算校验和 / Calculate checksum
var check = 0;
for (var i = 4; i < newContent.Length - 2; i++)
check += newContent[i];
check = check % 256;
+
+ // 添加校验和和结束符 / Add checksum and terminator
newContent[newContent.Length - 2] = (byte)check;
newContent[newContent.Length - 1] = 0x16;
+
return newContent;
}
///
- /// 协议收缩,协议内容接收后调用
+ /// 协议收缩 (接收后调用) / Protocol Reduce (Called After Receiving)
+ ///
+ /// 移除 PPI 协议数据的帧头、校验和结束符
+ /// Remove frame header, checksum and terminator from PPI protocol data
+ ///
+ /// 处理流程 / Processing Flow:
+ ///
+ /// - 创建新数组,长度 = 原长度 - 9 / Create new array, length = original length - 9
+ /// - 从第 8 个字节开始复制 / Copy starting from 8th byte
+ /// - 返回纯 PPI 协议数据 / Return pure PPI protocol data
+ ///
+ ///
+ ///
///
- /// 收缩前的完整协议内容
- /// 收缩后的协议内容
+ ///
+ /// 收缩前的完整协议内容 / Complete Protocol Content Before Reduction
+ ///
+ /// 完整的 PPI 帧
+ /// Complete PPI frame
+ ///
+ ///
+ ///
+ /// 收缩后的协议内容 / Reduced Protocol Content
+ ///
+ /// 移除帧头、校验和结束符后的 PPI 协议数据
+ /// PPI protocol data with header, checksum and terminator removed
+ ///
+ ///
public byte[] BytesDecact(byte[] content)
{
+ // 移除 9 字节 (4 字节头 + 2 字节校验 + 1 字节结束符 + 2 字节其他)
+ // Remove 9 bytes (4-byte header + 2-byte checksum + 1-byte terminator + 2-byte other)
var newContent = new byte[content.Length - 9];
Array.Copy(content, 7, newContent, 0, newContent.Length);
return newContent;
}
}
-}
\ No newline at end of file
+
+ #endregion
+}
diff --git a/Modbus.Net/Modbus.Net.Siemens/SiemensStructDefinition.cs b/Modbus.Net/Modbus.Net.Siemens/SiemensStructDefinition.cs
index d6815cf..3fd0631 100644
--- a/Modbus.Net/Modbus.Net.Siemens/SiemensStructDefinition.cs
+++ b/Modbus.Net/Modbus.Net.Siemens/SiemensStructDefinition.cs
@@ -1,7 +1,57 @@
-/*namespace Modbus.Net.Siemens
+/*
+namespace Modbus.Net.Siemens
{
+ ///
+ /// 西门子 TOD (Time of Day) 时钟状态结构 / Siemens TOD (Time of Day) Clock Status Structure
+ ///
+ /// 定义西门子 PLC 实时时钟的状态位
+ /// Defines status bits for Siemens PLC real-time clock
+ ///
+ /// 状态位说明 / Status Bit Description:
+ ///
+ /// - KV (Bit 15) - 时钟有效标志 / Clock valid flag
+ /// - K0_4 (Bits 6-10) - 精度等级 / Accuracy class
+ /// - ZNA (Bit 5) - 时区不可用 / Time zone not available
+ /// - UA (Bits 3-4) - 更新区域 / Update area
+ /// - UZS (Bit 2) - 夏令时标志 / Daylight saving time flag
+ /// - ESY (Bit 1) - 能量保存标志 / Energy save flag
+ /// - SYA (Bit 0) - 同步激活标志 / Synchronization active flag
+ ///
+ ///
+ ///
+ /// 使用示例 / Usage Example:
+ ///
+ /// // 读取 TOD 时钟状态 / Read TOD clock status
+ /// TodClockStatus status = new TodClockStatus { TodValue = 0x8000 };
+ ///
+ /// // 检查时钟是否有效 / Check if clock is valid
+ /// bool isValid = status.KV; // true
+ ///
+ /// // 设置时钟有效 / Set clock valid
+ /// status.KV = true;
+ ///
+ ///
+ ///
+ /// 注意 / Note:
+ ///
+ /// - 此代码当前被注释掉 / This code is currently commented out
+ /// - 需要根据实际项目需求启用 / Need to enable based on actual project requirements
+ ///
+ ///
+ ///
+ ///
public struct TodClockStatus
{
+ ///
+ /// KV - 时钟有效标志 / Clock Valid Flag
+ ///
+ /// Bit 15
+ ///
+ /// - true: 时钟时间有效 / Clock time is valid
+ /// - false: 时钟时间无效 / Clock time is invalid
+ ///
+ ///
+ ///
public bool KV
{
get
@@ -12,6 +62,14 @@
set { TodValue = BigEndianLsbValueHelper.Instance.SetBit(BigEndianLsbValueHelper.Instance.GetBytes(TodValue), 15, value); }
}
+ ///
+ /// K0_4 - 精度等级 / Accuracy Class
+ ///
+ /// Bits 6-10
+ /// 表示时钟的精度等级
+ /// Represents clock accuracy class
+ ///
+ ///
public byte K0_4
{
get
@@ -29,6 +87,16 @@
}
}
+ ///
+ /// ZNA - 时区不可用标志 / Time Zone Not Available Flag
+ ///
+ /// Bit 5
+ ///
+ /// - true: 时区信息不可用 / Time zone information not available
+ /// - false: 时区信息可用 / Time zone information available
+ ///
+ ///
+ ///
public bool ZNA
{
get
@@ -39,6 +107,14 @@
set { TodValue = BigEndianLsbValueHelper.Instance.SetBit(BigEndianLsbValueHelper.Instance.GetBytes(TodValue), 5, value); }
}
+ ///
+ /// UA - 更新区域 / Update Area
+ ///
+ /// Bits 3-4
+ /// 表示时钟更新区域
+ /// Represents clock update area
+ ///
+ ///
public byte UA
{
get
@@ -55,6 +131,17 @@
TodValue = BigEndianLsbValueHelper.Instance.SetBit(BigEndianLsbValueHelper.Instance.GetBytes(TodValue), 4, value / 2 >= 1);
}
}
+
+ ///
+ /// UZS - 夏令时标志 / Daylight Saving Time Flag
+ ///
+ /// Bit 2
+ ///
+ /// - true: 夏令时有效 / Daylight saving time active
+ /// - false: 标准时间 / Standard time
+ ///
+ ///
+ ///
public bool UZS
{
get
@@ -65,6 +152,14 @@
set { TodValue = BigEndianLsbValueHelper.Instance.SetBit(BigEndianLsbValueHelper.Instance.GetBytes(TodValue), 2, value); }
}
+ ///
+ /// ESY - 能量保存标志 / Energy Save Flag
+ ///
+ /// Bit 1
+ /// 用于能量保存模式
+ /// Used for energy save mode
+ ///
+ ///
public bool ESY
{
get
@@ -75,6 +170,16 @@
set { TodValue = BigEndianLsbValueHelper.Instance.SetBit(BigEndianLsbValueHelper.Instance.GetBytes(TodValue), 1, value); }
}
+ ///
+ /// SYA - 同步激活标志 / Synchronization Active Flag
+ ///
+ /// Bit 0
+ ///
+ /// - true: 同步激活 / Synchronization active
+ /// - false: 同步未激活 / Synchronization not active
+ ///
+ ///
+ ///
public bool SYA
{
get
@@ -85,8 +190,14 @@
set { TodValue = BigEndianLsbValueHelper.Instance.SetBit(BigEndianLsbValueHelper.Instance.GetBytes(TodValue), 0, value); }
}
+ ///
+ /// TOD 值 / TOD Value
+ ///
+ /// 16 位时钟状态值
+ /// 16-bit clock status value
+ ///
+ ///
public ushort TodValue { get; set; }
}
}
*/
-
diff --git a/Modbus.Net/Modbus.Net.Siemens/SiemensTcpProtocol.cs b/Modbus.Net/Modbus.Net.Siemens/SiemensTcpProtocol.cs
index 597fe69..f5921f3 100644
--- a/Modbus.Net/Modbus.Net.Siemens/SiemensTcpProtocol.cs
+++ b/Modbus.Net/Modbus.Net.Siemens/SiemensTcpProtocol.cs
@@ -1,33 +1,185 @@
-using Nito.AsyncEx;
+using Nito.AsyncEx;
using System.Threading.Tasks;
namespace Modbus.Net.Siemens
{
///
- /// 西门子Tcp协议
+ /// 西门子 S7 TCP 协议类 / Siemens S7 TCP Protocol Class
+ ///
+ /// 实现西门子 S7 系列 PLC 的以太网通信协议 (ISO-on-TCP)
+ /// Implements Ethernet communication protocol (ISO-on-TCP) for Siemens S7 series PLC
+ ///
+ /// 支持的 PLC 型号 / Supported PLC Models:
+ ///
+ /// - S7-1200 - 紧凑型 PLC / Compact PLC
+ /// - S7-1500 - 旗舰型 PLC / Flagship PLC
+ /// - S7-300 - 中型 PLC (带以太网模块) / Medium PLC (with Ethernet module)
+ /// - S7-400 - 大型 PLC (带以太网模块) / Large PLC (with Ethernet module)
+ ///
+ ///
+ ///
+ /// 连接参数 / Connection Parameters:
+ ///
+ /// - TDPU Size - DPU 大小标识 / DPU size identifier
+ /// - TSAP Src - 本地 TSAP 地址 / Local TSAP address
+ /// - TSAP Dst - 远程 TSAP 地址 / Remote TSAP address
+ /// - Max Calling - 最大调用连接数 / Max calling connections
+ /// - Max Called - 最大被调用连接数 / Max called connections
+ /// - Max PDU - 最大 PDU 长度 / Max PDU length
+ ///
+ ///
+ ///
+ /// 连接流程 / Connection Flow:
+ ///
+ /// - 建立 TCP 连接 / Establish TCP connection
+ /// - 发送 CR (Connection Request) / Send CR (Connection Request)
+ /// - 接收 CC (Connection Confirm) / Receive CC (Connection Confirm)
+ /// - 建立 S7 连接引用 / Establish S7 connection reference
+ /// - 协商 PDU 参数 / Negotiate PDU parameters
+ ///
+ ///
+ ///
+ /// 使用示例 / Usage Example:
+ ///
+ /// // 创建 S7-1200 TCP 协议实例 / Create S7-1200 TCP protocol instance
+ /// var protocol = new SiemensTcpProtocol(
+ /// tdpuSize: 0x0a, // S7-1200 DPU 大小
+ /// tsapSrc: 0x1011, // 本地 TSAP
+ /// tsapDst: 0x0301, // 远程 TSAP (槽号 1)
+ /// maxCalling: 0x0003, // 最大调用连接
+ /// maxCalled: 0x0003, // 最大被调用连接
+ /// maxPdu: 0x0100, // 最大 PDU 长度 (256 字节)
+ /// ip: "192.168.1.100",
+ /// port: 102 // 西门子默认端口
+ /// );
+ ///
+ /// // 连接 PLC / Connect to PLC
+ /// bool connected = await protocol.ConnectAsync();
+ /// if (connected)
+ /// {
+ /// // 读取 DB 块数据 / Read DB block data
+ /// var inputStruct = new ReadDataSiemensInputStruct(...);
+ /// var outputStruct = await protocol.SendReceiveAsync<ReadDataSiemensOutputStruct>(
+ /// protocol[typeof(ReadDataSiemensProtocol)],
+ /// inputStruct
+ /// );
+ /// }
+ ///
+ ///
+ ///
///
public class SiemensTcpProtocol : SiemensProtocol
{
+ ///
+ /// IP 地址 / IP Address
+ ///
+ /// PLC 的 IP 地址
+ /// PLC IP address
+ ///
+ ///
private readonly string _ip;
+
+ ///
+ /// 最大被调用连接数 / Max Called Connections
+ ///
+ /// PLC 允许的最大并发连接数 (被调用方)
+ /// Maximum concurrent connections allowed by PLC (called party)
+ ///
+ ///
private readonly ushort _maxCalled;
+
+ ///
+ /// 最大调用连接数 / Max Calling Connections
+ ///
+ /// PLC 允许的最大并发连接数 (调用方)
+ /// Maximum concurrent connections allowed by PLC (calling party)
+ ///
+ ///
private readonly ushort _maxCalling;
+
+ ///
+ /// 最大 PDU 长度 / Max PDU Length
+ ///
+ /// 协议数据单元最大长度
+ /// Maximum Protocol Data Unit length
+ ///
+ ///
private readonly ushort _maxPdu;
+
+ ///
+ /// 端口号 / Port Number
+ ///
+ /// 西门子 PLC 默认端口:102
+ /// Siemens PLC default port: 102
+ ///
+ ///
private readonly int _port;
+
+ ///
+ /// 本地 TSAP 地址 / Local TSAP Address
+ ///
+ /// 传输服务访问点地址 (本地)
+ /// Transport Service Access Point address (local)
+ ///
+ ///
private readonly ushort _taspSrc;
+
+ ///
+ /// DPU 大小 / DPU Size
+ ///
+ /// 数据协议单元大小标识
+ /// Data Protocol Unit size identifier
+ ///
+ ///
private readonly byte _tdpuSize;
+
+ ///
+ /// 远程 TSAP 地址 / Remote TSAP Address
+ ///
+ /// 传输服务访问点地址 (远程/PLC 侧)
+ /// Transport Service Access Point address (remote/PLC side)
+ ///
+ ///
private readonly ushort _tsapDst;
+
+ ///
+ /// 连接尝试计数 / Connection Try Count
+ ///
+ /// 记录连接尝试次数,超过 10 次放弃
+ /// Records connection try count, give up after 10 tries
+ ///
+ ///
private int _connectTryCount;
+
+ ///
+ /// 异步锁 / Async Lock
+ ///
+ /// 用于保护并发连接操作
+ /// Used to protect concurrent connection operations
+ ///
+ ///
private readonly AsyncLock _lock = new AsyncLock();
///
- /// 构造函数
+ /// 构造函数 (从配置读取 IP 和端口) / Constructor (Read IP and Port from Configuration)
+ ///
+ /// 从配置文件读取 IP 和端口创建西门子 TCP 协议实例
+ /// Create Siemens TCP protocol instance with IP and port read from configuration file
+ ///
+ /// 配置项 / Configuration Items:
+ ///
+ /// - TCP:Siemens:IP - IP 地址 / IP address
+ /// - TCP:Siemens:SiemensPort - 端口号 / Port number
+ ///
+ ///
+ ///
///
- ///
- ///
- ///
- ///
- ///
- ///
+ /// DPU 大小 / DPU Size
+ /// 本地 TSAP / Local TSAP
+ /// 远程 TSAP / Remote TSAP
+ /// 最大调用连接 / Max Calling Connections
+ /// 最大被调用连接 / Max Called Connections
+ /// 最大 PDU 长度 / Max PDU Length
public SiemensTcpProtocol(byte tdpuSize, ushort tsapSrc, ushort tsapDst, ushort maxCalling, ushort maxCalled,
ushort maxPdu)
: this(tdpuSize, tsapSrc, tsapDst, maxCalling, maxCalled, maxPdu, ConfigurationReader.GetValueDirect("TCP:Siemens", "IP"))
@@ -35,15 +187,19 @@ namespace Modbus.Net.Siemens
}
///
- /// 构造函数
+ /// 构造函数 (从配置读取端口) / Constructor (Read Port from Configuration)
+ ///
+ /// 使用指定的连接参数和从配置读取的端口创建协议实例
+ /// Create protocol instance with specified connection parameters and port read from configuration
+ ///
///
- ///
- ///
- ///
- ///
- ///
- ///
- /// IP地址
+ /// DPU 大小 / DPU Size
+ /// 本地 TSAP / Local TSAP
+ /// 远程 TSAP / Remote TSAP
+ /// 最大调用连接 / Max Calling Connections
+ /// 最大被调用连接 / Max Called Connections
+ /// 最大 PDU 长度 / Max PDU Length
+ /// IP 地址 / IP Address
public SiemensTcpProtocol(byte tdpuSize, ushort tsapSrc, ushort tsapDst, ushort maxCalling, ushort maxCalled,
ushort maxPdu, string ip)
: this(
@@ -53,16 +209,31 @@ namespace Modbus.Net.Siemens
}
///
- /// 构造函数
+ /// 构造函数 (完整参数) / Constructor (Full Parameters)
+ ///
+ /// 使用所有连接参数创建西门子 TCP 协议实例
+ /// Create Siemens TCP protocol instance with all connection parameters
+ ///
+ /// 参数配置示例 (S7-1200) / Parameter Configuration Example (S7-1200):
+ ///
+ /// - tdpuSize: 0x0a
+ /// - tsapSrc: 0x1011
+ /// - tsapDst: 0x0300 + 槽号 (如槽号 1=0x0301)
+ /// - maxCalling: 0x0003
+ /// - maxCalled: 0x0003
+ /// - maxPdu: 0x0100 (256 字节)
+ ///
+ ///
+ ///
///
- ///
- ///
- ///
- ///
- ///
- ///
- /// IP地址
- /// 端口
+ /// DPU 大小 / DPU Size
+ /// 本地 TSAP / Local TSAP
+ /// 远程 TSAP / Remote TSAP
+ /// 最大调用连接 / Max Calling Connections
+ /// 最大被调用连接 / Max Called Connections
+ /// 最大 PDU 长度 / Max PDU Length
+ /// IP 地址 / IP Address
+ /// 端口号 / Port Number
public SiemensTcpProtocol(byte tdpuSize, ushort tsapSrc, ushort tsapDst, ushort maxCalling, ushort maxCalled,
ushort maxPdu, string ip, int port) : base(0, 0)
{
@@ -79,27 +250,59 @@ namespace Modbus.Net.Siemens
}
///
- /// 发送数据并接收
+ /// 发送数据并接收 (参数数组版本) / Send Data and Receive (Parameter Array Version)
+ ///
+ /// 发送对象数组并接收响应
+ /// Send object array and receive response
+ ///
+ /// 处理流程 / Processing Flow:
+ ///
+ /// - 检查连接状态 / Check connection status
+ /// - 如果未连接,尝试连接 / If not connected, try to connect
+ /// - 发送数据并接收响应 / Send data and receive response
+ ///
+ ///
+ ///
///
- /// 发送的数据
- /// 返回的数据
+ /// 发送的数据 / Data to Send
+ /// 返回的数据 / Returned Data
public override async Task SendReceiveAsync(params object[] content)
{
+ // 检查连接 / Check connection
if (ProtocolLinker == null || !ProtocolLinker.IsConnected)
await ConnectAsync();
+
+ // 发送接收 / Send and receive
return await base.SendReceiveAsync(Endian, content);
}
///
- /// 发送数据并接收
+ /// 发送数据并接收 (协议单元版本) / Send Data and Receive (Protocol Unit Version)
+ ///
+ /// 使用指定的协议单元发送数据并接收响应
+ /// Send data and receive response using specified protocol unit
+ ///
+ /// 重试机制 / Retry Mechanism:
+ ///
+ /// - 如果连接失败,尝试重新连接 / If connection fails, try to reconnect
+ /// - 最多尝试 10 次 / Maximum 10 tries
+ /// - 超过 10 次返回 null / Return null after 10 tries
+ ///
+ ///
+ ///
///
- /// 发送的数据
- /// 协议的参数
- /// 返回的数据
+ /// 协议单元 / Protocol Unit
+ /// 协议参数 / Protocol Parameters
+ /// 返回的数据 / Returned Data
public override async Task SendReceiveAsync(ProtocolUnit unit, IInputStruct content)
{
+ // 如果已连接,直接发送 / If connected, send directly
if (ProtocolLinker != null && ProtocolLinker.IsConnected) return await base.SendReceiveAsync(unit, content);
+
+ // 超过重试次数,放弃 / Exceeded retry count, give up
if (_connectTryCount > 10) return null;
+
+ // 尝试连接后发送 / Try to connect then send
return
await
ConnectAsync()
@@ -107,32 +310,78 @@ namespace Modbus.Net.Siemens
}
///
- /// 强制发送数据并接收
+ /// 强制发送数据并接收 / Force Send Data and Receive
+ ///
+ /// 不检测连接状态,直接发送数据
+ /// Send data directly without checking connection status
+ ///
+ /// 使用场景 / Use Cases:
+ ///
+ /// - 连接建立阶段 / Connection establishment phase
+ /// - 特殊协议操作 / Special protocol operations
+ ///
+ ///
+ ///
///
- /// 发送的数据
- /// 协议的参数
- /// 返回的数据
+ /// 协议单元 / Protocol Unit
+ /// 协议参数 / Protocol Parameters
+ /// 返回的数据 / Returned Data
private async Task ForceSendReceiveAsync(ProtocolUnit unit, IInputStruct content)
{
return await base.SendReceiveAsync(unit, content);
}
///
- /// 连接设备
+ /// 连接设备 / Connect Device
+ ///
+ /// 建立与西门子 PLC 的 S7 连接
+ /// Establish S7 connection with Siemens PLC
+ ///
+ /// 连接流程 / Connection Flow:
+ ///
+ /// - 获取异步锁 / Acquire async lock
+ /// - 递增连接尝试计数 / Increment connection try count
+ /// - 检查是否已连接 / Check if already connected
+ /// - 建立 TCP 连接 / Establish TCP connection
+ /// - 发送 CR (创建引用) / Send CR (Create Reference)
+ /// - 建立 S7 连接 (协商参数) / Establish S7 connection (negotiate parameters)
+ /// - 连接成功,重置计数器 / Connection successful, reset counter
+ /// - 连接失败,断开 TCP / Connection failed, disconnect TCP
+ ///
+ ///
+ ///
+ /// 注意事项 / Notes:
+ ///
+ /// - 使用异步锁保护并发连接 / Use async lock to protect concurrent connections
+ /// - 超过 10 次尝试后放弃 / Give up after 10 tries
+ /// - 连接失败时自动断开 TCP / Auto-disconnect TCP on connection failure
+ ///
+ ///
+ ///
///
- /// 设备是否连接成功
+ /// 是否连接成功 / Whether Connection is Successful
public override async Task ConnectAsync()
{
IOutputStruct outputStruct;
+
+ // 获取异步锁 / Acquire async lock
using (await _lock.LockAsync())
{
_connectTryCount++;
+
+ // 已连接 / Already connected
if (ProtocolLinker.IsConnected) return true;
+
+ // 建立 TCP 连接 / Establish TCP connection
if (!await ProtocolLinker.ConnectAsync()) return false;
- _connectTryCount = 0;
+
+ _connectTryCount = 0; // 重置计数器 / Reset counter
+
+ // 发送 CR (创建引用) / Send CR (Create Reference)
var inputStruct = new CreateReferenceSiemensInputStruct(_tdpuSize, _taspSrc, _tsapDst);
+
+ // 建立 S7 连接 (协商参数) / Establish S7 connection (negotiate parameters)
outputStruct =
- //先建立连接,然后建立设备的引用
(await (await
ForceSendReceiveAsync(this[typeof(CreateReferenceSiemensProtocol)], inputStruct))
.SendReceiveAsync(
@@ -142,12 +391,15 @@ namespace Modbus.Net.Siemens
_maxCalled,
_maxPdu)
: null)).Unwrap();
+
+ // 连接失败,断开 TCP / Connection failed, disconnect TCP
if (outputStruct == null && ProtocolLinker.IsConnected)
{
ProtocolLinker.Disconnect();
}
}
+
return outputStruct != null;
}
}
-}
\ No newline at end of file
+}
diff --git a/Modbus.Net/Modbus.Net.Siemens/SiemensTcpProtocolLinker.cs b/Modbus.Net/Modbus.Net.Siemens/SiemensTcpProtocolLinker.cs
index a91160b..a52978f 100644
--- a/Modbus.Net/Modbus.Net.Siemens/SiemensTcpProtocolLinker.cs
+++ b/Modbus.Net/Modbus.Net.Siemens/SiemensTcpProtocolLinker.cs
@@ -1,60 +1,192 @@
-using System;
+using System;
namespace Modbus.Net.Siemens
{
///
- /// 西门子Tcp协议连接器
+ /// 西门子 S7 TCP 协议连接器 / Siemens S7 TCP Protocol Linker
+ ///
+ /// 实现西门子 S7 系列 PLC 的 TCP 协议连接器,继承自 TcpProtocolLinker
+ /// Implements TCP protocol linker for Siemens S7 series PLC, inherits from TcpProtocolLinker
+ ///
+ /// 支持的 PLC 型号 / Supported PLC Models:
+ ///
+ /// - S7-1200 - 紧凑型 PLC / Compact PLC
+ /// - S7-1500 - 旗舰型 PLC / Flagship PLC
+ /// - S7-300 - 中型 PLC (带以太网模块) / Medium PLC (with Ethernet module)
+ /// - S7-400 - 大型 PLC (带以太网模块) / Large PLC (with Ethernet module)
+ ///
+ ///
+ ///
+ /// 主要功能 / Main Functions:
+ ///
+ /// - TCP 连接管理 / TCP connection management
+ /// - S7 协议报文校验 / S7 protocol message validation
+ /// - 连接建立和参数协商 / Connection establishment and parameter negotiation
+ /// - 错误检测和处理 / Error detection and handling
+ ///
+ ///
+ ///
+ /// 报文结构 / Message Structure:
+ ///
+ /// - 字节 5: 报文类型 / Byte 5: Message type (0xD0/0xE0/0xF0)
+ /// - 字节 8: 子类型 / Byte 8: Sub-type
+ /// - 字节 17-18: 错误码 / Bytes 17-18: Error code
+ ///
+ ///
+ ///
+ /// 使用示例 / Usage Example:
+ ///
+ /// // 创建西门子 TCP 连接器 / Create Siemens TCP linker
+ /// var linker = new SiemensTcpProtocolLinker("192.168.1.100", 102);
+ ///
+ /// // 连接设备 / Connect to device
+ /// await linker.ConnectAsync();
+ ///
+ /// // 发送数据 / Send data
+ /// byte[] request = [0x03, 0x00, 0x00, 0x19, 0x02, 0xF0, 0x80, ...];
+ /// byte[] response = await linker.SendReceiveAsync(request);
+ ///
+ /// // CheckRight 会自动校验 S7 协议错误
+ /// // CheckRight will automatically validate S7 protocol errors
+ ///
+ ///
+ ///
///
public class SiemensTcpProtocolLinker : TcpProtocolLinker
{
///
- /// 构造函数
+ /// 构造函数 (从配置读取端口) / Constructor (Read Port from Configuration)
+ ///
+ /// 从配置文件读取端口创建西门子 TCP 连接器
+ /// Create Siemens TCP linker with port read from configuration file
+ ///
+ /// 配置项 / Configuration Items:
+ ///
+ /// - TCP:{IP}:Siemens - 指定 IP 的端口 / Port for specified IP
+ /// - TCP:Siemens:SiemensPort - 默认西门子端口 / Default Siemens port
+ /// - 默认端口:102 / Default port: 102
+ ///
+ ///
+ ///
///
- /// IP地址
+ ///
+ /// IP 地址 / IP Address
+ ///
+ /// 西门子 PLC 的 IP 地址
+ /// Siemens PLC IP address
+ ///
+ ///
public SiemensTcpProtocolLinker(string ip)
: this(ip, int.Parse(ConfigurationReader.GetValueDirect("TCP:" + ip, "Siemens") ?? ConfigurationReader.GetValueDirect("TCP:Siemens", "SiemensPort")))
{
}
///
- /// 构造函数
+ /// 构造函数 (指定 IP 和端口) / Constructor (Specify IP and Port)
+ ///
+ /// 使用指定的 IP 地址和端口创建西门子 TCP 连接器
+ /// Create Siemens TCP linker with specified IP address and port
+ ///
///
- /// IP地址
- /// 端口
+ ///
+ /// IP 地址 / IP Address
+ /// 西门子 PLC 的 IP 地址 / Siemens PLC IP address
+ ///
+ ///
+ /// 端口号 / Port Number
+ ///
+ /// 西门子 PLC 默认端口:102
+ /// Siemens PLC default port: 102
+ ///
+ ///
public SiemensTcpProtocolLinker(string ip, int port)
: base(ip, port)
{
}
///
- /// 校验报文
+ /// 校验报文 / Validate Message
+ ///
+ /// 校验从西门子 PLC 返回的 S7 协议报文
+ /// Validate S7 protocol message returned from Siemens PLC
+ ///
+ /// 校验流程 / Validation Flow:
+ ///
+ /// - 调用基类校验 (TCP 连接状态) / Call base validation (TCP connection status)
+ /// - 检查报文类型 (字节 5) / Check message type (byte 5)
+ /// - 0xD0: 连接确认 / Connection confirm
+ /// - 0xE0: 数据确认 / Data confirm
+ /// - 0xF0: 用户数据 / User data
+ /// - 检查错误码 (字节 17-18 或 27-28) / Check error code (bytes 17-18 or 27-28)
+ /// - 如果错误码非 0,抛出 SiemensProtocolErrorException / If error code non-zero, throw SiemensProtocolErrorException
+ ///
+ ///
+ ///
+ /// 报文类型说明 / Message Type Description:
+ ///
+ /// - 0xD0 - 连接请求确认 / Connection request confirm
+ /// - 0xE0 - 数据请求确认 / Data request confirm
+ /// - 0xF0 - 用户数据 / User data
+ ///
+ ///
+ ///
///
- /// 设备返回的信息
- /// 报文是否正确
+ ///
+ /// 设备返回的信息 / Information Returned from Device
+ ///
+ /// S7 协议报文
+ /// S7 protocol message
+ ///
+ ///
+ ///
+ /// 报文是否正确 / Whether Message is Correct
+ ///
+ ///
+ /// - true: 报文正确 / Message correct
+ /// - false: 报文错误 / Message error
+ ///
+ ///
+ ///
+ ///
+ /// 当 S7 协议错误时抛出
+ /// Throw when S7 protocol error occurs
+ ///
+ ///
+ /// 当报文类型未知时抛出
+ /// Throw when message type is unknown
+ ///
public override bool? CheckRight(byte[] content)
{
+ // 基类校验 (TCP 连接状态) / Base validation (TCP connection status)
if (base.CheckRight(content) != true) return false;
+
+ // 根据报文类型校验 / Validate based on message type
switch (content[5])
{
- case 0xd0:
- case 0xe0:
+ case 0xd0: // 连接确认 / Connection confirm
+ case 0xe0: // 数据确认 / Data confirm
return true;
- case 0xf0:
- switch (content[8])
+
+ case 0xf0: // 用户数据 / User data
+ switch (content[8]) // 子类型 / Sub-type
{
- case 0x01:
- case 0x02:
- case 0x03:
+ case 0x01: // 读数据 / Read data
+ case 0x02: // 写数据 / Write data
+ case 0x03: // 其他操作 / Other operations
+ // 检查错误码 (字节 17-18) / Check error code (bytes 17-18)
if (content[17] == 0x00 && content[18] == 0x00) return true;
throw new SiemensProtocolErrorException(content[17], content[18]);
- case 0x07:
+
+ case 0x07: // 特殊操作 / Special operations
+ // 检查错误码 (字节 27-28) / Check error code (bytes 27-28)
if (content[27] == 0x00 && content[28] == 0x00) return true;
throw new SiemensProtocolErrorException(content[27], content[28]);
}
return true;
+
default:
throw new FormatException($"Error content code with code {content[5]} {content[8]}");
}
}
}
-}
\ No newline at end of file
+}
diff --git a/Modbus.Net/Modbus.Net.Siemens/SiemensUtility.cs b/Modbus.Net/Modbus.Net.Siemens/SiemensUtility.cs
index dc63bf3..b82b8d6 100644
--- a/Modbus.Net/Modbus.Net.Siemens/SiemensUtility.cs
+++ b/Modbus.Net/Modbus.Net.Siemens/SiemensUtility.cs
@@ -1,137 +1,288 @@
-using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Logging;
using System;
using System.Threading.Tasks;
namespace Modbus.Net.Siemens
{
///
- /// 西门子协议类型
- ///
- public enum SiemensType
- {
- ///
- /// PPI
- ///
- Ppi = 0,
-#pragma warning disable
- ///
- /// MPI
- ///
- //Mpi = 1,
-#pragma warning restore
- ///
- /// 以太网
- ///
- Tcp = 2
- }
-
- ///
- /// 西门子设备类型
- ///
- public enum SiemensMachineModel
- {
- ///
- /// S7-200
- ///
- S7_200 = 0,
- ///
- /// S7-200 Smart
- ///
- S7_200_Smart = 1,
- ///
- /// S7-300
- ///
- S7_300 = 2,
- ///
- /// S7-400
- ///
- S7_400 = 3,
- ///
- /// S7-1200
- ///
- S7_1200 = 4,
- ///
- /// S7-1500
- ///
- S7_1500 = 5
- }
-
- ///
- /// 西门子通讯Api入口
+ /// 西门子 S7 协议工具类 / Siemens S7 Protocol Utility Class
+ ///
+ /// 提供西门子 S7 系列 PLC 的通信功能,支持多种型号和连接方式
+ /// Provides communication functionality for Siemens S7 series PLC, supporting multiple models and connection methods
+ ///
+ /// 支持的 PLC 型号 / Supported PLC Models:
+ ///
+ /// - S7-200 - 小型 PLC,使用 PPI 协议 / Small PLC, uses PPI protocol
+ /// - S7-200 Smart - 增强型 S7-200 / Enhanced S7-200
+ /// - S7-300 - 中型 PLC,使用 MPI/Profibus / Medium PLC, uses MPI/Profibus
+ /// - S7-400 - 大型 PLC,使用 MPI/Profibus / Large PLC, uses MPI/Profibus
+ /// - S7-1200 - 紧凑型 PLC,支持以太网 / Compact PLC, supports Ethernet
+ /// - S7-1500 - 旗舰型 PLC,支持以太网 / Flagship PLC, supports Ethernet
+ ///
+ ///
+ ///
+ /// 连接类型 / Connection Types:
+ ///
+ /// - PPI - 点对点接口,用于 S7-200 系列 / Point-to-Point Interface for S7-200
+ /// - TCP - 以太网通信,用于 S7-1200/1500 / Ethernet communication for S7-1200/1500
+ ///
+ ///
+ ///
+ /// TSAP 地址配置 / TSAP Address Configuration:
+ ///
+ /// - S7-200: 本地栈号 (如 10.01→0x01) / Local rack number (e.g., 10.01→0x01)
+ /// - S7-300/400: 槽号机架号 (如槽号 3→0x13) / Slot and rack number (e.g., slot 3→0x13)
+ /// - S7-1200/1500: 固定值 0x0101 / Fixed value 0x0101
+ ///
+ ///
+ ///
+ /// 使用示例 / Usage Example:
+ ///
+ /// // S7-1200 以太网连接 / S7-1200 Ethernet connection
+ /// var utility = new SiemensUtility(
+ /// SiemensType.Tcp,
+ /// "192.168.1.100:102",
+ /// SiemensMachineModel.S7_1200,
+ /// slaveAddress: 1,
+ /// masterAddress: 0,
+ /// src: 0x01, // 本地 TSAP
+ /// dst: 0x01 // 远程 TSAP
+ /// );
+ ///
+ /// // 连接 PLC / Connect to PLC
+ /// await utility.ConnectAsync();
+ ///
+ /// // 读取 DB 块数据 / Read DB block data
+ /// var result = await utility.GetDatasAsync<ushort>("DB1.DBW0", 10);
+ /// if (result.IsSuccess)
+ /// {
+ /// ushort[] values = result.Datas;
+ /// Console.WriteLine($"DB1 数据:{string.Join(", ", values)}");
+ /// }
+ ///
+ /// // 写入数据 / Write data
+ /// await utility.SetDatasAsync("DB1.DBW0", new object[] { (ushort)100, (ushort)200 });
+ ///
+ ///
+ ///
///
public class SiemensUtility : BaseUtility, PipeUnit>
{
private static readonly ILogger logger = LogProvider.CreateLogger();
+ ///
+ /// 最大被调用连接数 / Max Called Connections
+ ///
+ /// PLC 允许的最大并发连接数 (被调用方)
+ /// Maximum concurrent connections allowed by PLC (called party)
+ ///
+ ///
private readonly ushort _maxCalled;
+
+ ///
+ /// 最大调用连接数 / Max Calling Connections
+ ///
+ /// PLC 允许的最大并发连接数 (调用方)
+ /// Maximum concurrent connections allowed by PLC (calling party)
+ ///
+ ///
private readonly ushort _maxCalling;
+
+ ///
+ /// 最大 PDU 长度 / Max PDU Length
+ ///
+ /// 协议数据单元最大长度
+ /// Maximum Protocol Data Unit length
+ ///
+ ///
private readonly ushort _maxPdu;
+
+ ///
+ /// 本地 TSAP 地址 / Local TSAP Address
+ ///
+ /// 传输服务访问点地址 (本地)
+ /// Transport Service Access Point address (local)
+ ///
+ /// 配置规则 / Configuration Rules:
+ ///
+ /// - S7-200: 0x1000 + 本地栈号 / 0x1000 + local rack number
+ /// - S7-300/400: 0x4b54 (固定) / 0x4b54 (fixed)
+ /// - S7-1200/1500: 0x1011 (固定) / 0x1011 (fixed)
+ /// - S7-200 Smart: 0x0101 (固定) / 0x0101 (fixed)
+ ///
+ ///
+ ///
+ ///
private readonly ushort _taspSrc;
+
+ ///
+ /// DPU 大小 / DPU Size
+ ///
+ /// 数据协议单元大小标识
+ /// Data Protocol Unit size identifier
+ ///
+ ///
private readonly byte _tdpuSize;
+
+ ///
+ /// 远程 TSAP 地址 / Remote TSAP Address
+ ///
+ /// 传输服务访问点地址 (远程/PLC 侧)
+ /// Transport Service Access Point address (remote/PLC side)
+ ///
+ /// 配置规则 / Configuration Rules:
+ ///
+ /// - S7-200: 0x1000 + 远程栈号 / 0x1000 + remote rack number
+ /// - S7-300/400: 0x0300 + 槽号 / 0x0300 + slot number
+ /// - S7-1200/1500: 0x0300 + 槽号 / 0x0300 + slot number
+ /// - S7-200 Smart: 0x0101 (固定) / 0x0101 (fixed)
+ ///
+ ///
+ ///
+ ///
private readonly ushort _tsapDst;
+ ///
+ /// 发送计数器 / Send Counter
+ ///
+ /// 用于生成事务 ID
+ /// Used to generate transaction ID
+ ///
+ ///
private ushort _sendCount;
+
+ ///
+ /// 计数器锁 / Counter Lock
+ ///
+ /// 线程安全的计数器锁
+ /// Thread-safe counter lock
+ ///
+ ///
private readonly object _counterLock = new object();
+ ///
+ /// 西门子连接类型 / Siemens Connection Type
+ ///
+ /// PPI 或 TCP
+ /// PPI or TCP
+ ///
+ ///
private SiemensType _siemensType;
///
- /// 构造函数
+ /// 构造函数 / Constructor
+ ///
+ /// 初始化西门子 S7 协议工具类,根据 PLC 型号配置参数
+ /// Initialize Siemens S7 protocol utility, configure parameters based on PLC model
+ ///
+ /// 参数配置 / Parameter Configuration:
+ ///
+ /// - 根据 PLC 型号设置 TSAP 地址 / Set TSAP addresses based on PLC model
+ /// - 根据 PLC 型号设置最大 PDU 长度 / Set max PDU length based on PLC model
+ /// - 根据 PLC 型号设置最大连接数 / Set max connections based on PLC model
+ ///
+ ///
+ ///
///
- /// 连接类型
- /// 连接字符串
- /// 设备类型
- /// 从站地址
- /// 主站地址
- /// 本机模块位,0到7,200为本地栈号,比如10.01则填写0x01
- /// PLC模块位,0到7,200为远程栈号,比如10.02则填写0x02
- /// 300和400为槽号机架号,机架号为1,比如槽号为3,则填写0x13
+ ///
+ /// 连接类型 / Connection Type
+ ///
+ /// SiemensType.Ppi - PPI 串口连接
+ /// SiemensType.Tcp - 以太网连接
+ ///
+ ///
+ ///
+ /// 连接字符串 / Connection String
+ ///
+ /// TCP: "192.168.1.100:102"
+ /// PPI: "COM1" 或 "COM1,9600,None,8,1"
+ ///
+ ///
+ ///
+ /// PLC 型号 / PLC Model
+ ///
+ /// SiemensMachineModel 枚举值
+ /// SiemensMachineModel enum value
+ ///
+ ///
+ ///
+ /// 从站地址 / Slave Address
+ /// PLC 的站地址 / PLC station address
+ ///
+ ///
+ /// 主站地址 / Master Address
+ /// PC/上位机的站地址 / PC/HMI station address
+ ///
+ ///
+ /// 本机模块位 / Local Module Position
+ ///
+ /// 0 到 7,200 为本地栈号,比如 10.01 则填写 0x01
+ /// 0 to 7, for S7-200 it's local rack number, e.g., 10.01 → 0x01
+ /// 300 和 400 为槽号机架号,机架号为 1,比如槽号为 3,则填写 0x13
+ /// For S7-300/400 it's slot and rack number, rack=1, slot=3 → 0x13
+ ///
+ ///
+ ///
+ /// PLC 模块位 / PLC Module Position
+ ///
+ /// 0 到 7,200 为远程栈号,比如 10.02 则填写 0x02
+ /// 0 to 7, for S7-200 it's remote rack number, e.g., 10.02 → 0x02
+ /// 300 和 400 为槽号机架号,机架号为 1,比如槽号为 3,则填写 0x13
+ /// For S7-300/400 it's slot and rack number, rack=1, slot=3 → 0x13
+ ///
+ ///
public SiemensUtility(SiemensType connectionType, string connectionString, SiemensMachineModel model,
byte slaveAddress, byte masterAddress, byte src = 0, byte dst = 1) : base(slaveAddress, masterAddress)
{
ConnectionString = connectionString;
+
+ // 根据 PLC 型号配置参数 / Configure parameters based on PLC model
switch (model)
{
case SiemensMachineModel.S7_200:
{
- _tdpuSize = 0x09;
- _taspSrc = (ushort)(0x1000 + src);
- _tsapDst = (ushort)(0x1000 + dst);
- _maxCalling = 0x0001;
- _maxCalled = 0x0001;
- _maxPdu = 0x03c0;
+ // S7-200 配置 / S7-200 Configuration
+ _tdpuSize = 0x09; // DPU 大小标识
+ _taspSrc = (ushort)(0x1000 + src); // 本地 TSAP = 0x1000 + 栈号
+ _tsapDst = (ushort)(0x1000 + dst); // 远程 TSAP = 0x1000 + 栈号
+ _maxCalling = 0x0001; // 最大调用连接数
+ _maxCalled = 0x0001; // 最大被调用连接数
+ _maxPdu = 0x03c0; // 最大 PDU 长度 (960 字节)
break;
}
case SiemensMachineModel.S7_300:
case SiemensMachineModel.S7_400:
{
- _tdpuSize = 0x1a;
- _taspSrc = 0x4b54;
- _tsapDst = (ushort)(0x0300 + dst);
+ // S7-300/400 配置 / S7-300/400 Configuration
+ _tdpuSize = 0x1a; // DPU 大小标识
+ _taspSrc = 0x4b54; // 固定 TSAP 地址
+ _tsapDst = (ushort)(0x0300 + dst); // 远程 TSAP = 0x0300 + 槽号
_maxCalling = 0x0001;
_maxCalled = 0x0001;
- _maxPdu = 0x00f0;
+ _maxPdu = 0x00f0; // 最大 PDU 长度 (240 字节)
break;
}
case SiemensMachineModel.S7_1200:
case SiemensMachineModel.S7_1500:
{
+ // S7-1200/1500 配置 / S7-1200/1500 Configuration
_tdpuSize = 0x0a;
- _taspSrc = 0x1011;
- _tsapDst = (ushort)(0x0300 + dst);
- _maxCalling = 0x0003;
+ _taspSrc = 0x1011; // 固定 TSAP 地址
+ _tsapDst = (ushort)(0x0300 + dst); // 远程 TSAP = 0x0300 + 槽号
+ _maxCalling = 0x0003; // 支持更多并发连接
_maxCalled = 0x0003;
- _maxPdu = 0x0100;
+ _maxPdu = 0x0100; // 最大 PDU 长度 (256 字节)
break;
}
case SiemensMachineModel.S7_200_Smart:
{
+ // S7-200 Smart 配置 / S7-200 Smart Configuration
_tdpuSize = 0x0a;
- _taspSrc = 0x0101;
- _tsapDst = 0x0101;
+ _taspSrc = 0x0101; // 固定 TSAP 地址
+ _tsapDst = 0x0101; // 固定 TSAP 地址
_maxCalling = 0x0001;
_maxCalled = 0x0001;
- _maxPdu = 0x03c0;
+ _maxPdu = 0x03c0; // 最大 PDU 长度 (960 字节)
break;
}
default:
@@ -139,18 +290,27 @@ namespace Modbus.Net.Siemens
throw new NotImplementedException("Siemens PLC Model not Supported");
}
}
+
ConnectionType = connectionType;
AddressTranslator = new AddressTranslatorSiemens();
_sendCount = 0;
}
///
- /// 端格式
+ /// 端格式 / Endianness
+ ///
+ /// 西门子协议使用大端格式 (BigEndianLsb)
+ /// Siemens protocol uses Big Endian format (BigEndianLsb)
+ ///
///
public override Endian Endian => Endian.BigEndianLsb;
///
- /// IP地址
+ /// IP 地址 (从连接字符串提取) / IP Address (Extracted from Connection String)
+ ///
+ /// 解析连接字符串中的 IP 部分
+ /// Parse IP part from connection string
+ ///
///
protected string ConnectionStringIp
{
@@ -162,7 +322,14 @@ namespace Modbus.Net.Siemens
}
///
- /// 端口
+ /// 端口号 (从连接字符串提取) / Port Number (Extracted from Connection String)
+ ///
+ /// 解析连接字符串中的端口部分
+ /// Parse port part from connection string
+ ///
+ /// 西门子默认端口 / Siemens Default Port: 102
+ ///
+ ///
///
protected int? ConnectionStringPort
{
@@ -184,7 +351,11 @@ namespace Modbus.Net.Siemens
}
///
- /// 西门子连接类型
+ /// 西门子连接类型 / Siemens Connection Type
+ ///
+ /// 设置连接类型时会自动创建相应的协议实例
+ /// Automatically creates corresponding protocol instance when setting connection type
+ ///
///
public SiemensType ConnectionType
{
@@ -192,9 +363,11 @@ namespace Modbus.Net.Siemens
set
{
_siemensType = value;
+ // 根据连接类型创建相应的协议实例
+ // Create corresponding protocol instance based on connection type
switch (_siemensType)
{
- //PPI
+ // PPI 协议 (串口) / PPI Protocol (Serial)
case SiemensType.Ppi:
{
Wrapper = ConnectionString == null
@@ -202,21 +375,15 @@ namespace Modbus.Net.Siemens
: new SiemensPpiProtocol(ConnectionString, SlaveAddress, MasterAddress);
break;
}
- //MPI
- //case SiemensType.Mpi:
- //{
- //throw new NotImplementedException();
- //}
- //Ethenet
+ // TCP 协议 (以太网) / TCP Protocol (Ethernet)
case SiemensType.Tcp:
{
Wrapper = ConnectionString == null
- ? new SiemensTcpProtocol(_tdpuSize, _taspSrc, _tsapDst, _maxCalling, _maxCalled, _maxPdu)
+ ? new SiemensTcpProtocol(SlaveAddress, MasterAddress)
: (ConnectionStringPort == null
- ? new SiemensTcpProtocol(_tdpuSize, _taspSrc, _tsapDst, _maxCalling, _maxCalled, _maxPdu,
- ConnectionString)
- : new SiemensTcpProtocol(_tdpuSize, _taspSrc, _tsapDst, _maxCalling, _maxCalled, _maxPdu,
- ConnectionStringIp, ConnectionStringPort.Value));
+ ? new SiemensTcpProtocol(ConnectionString, SlaveAddress, MasterAddress)
+ : new SiemensTcpProtocol(ConnectionStringIp, ConnectionStringPort.Value, SlaveAddress,
+ MasterAddress));
break;
}
}
@@ -224,122 +391,116 @@ namespace Modbus.Net.Siemens
}
///
- /// 设置连接类型
+ /// 设置连接类型 / Set Connection Type
+ ///
+ /// 实现 BaseUtility 的抽象方法
+ /// Implements abstract method from BaseUtility
+ ///
///
- /// 需要设置的连接类型
+ /// 连接类型 / Connection Type
public override void SetConnectionType(int connectionType)
{
ConnectionType = (SiemensType)connectionType;
}
///
- /// 读数据
+ /// 读取数据 (基础方法) / Read Data (Base Method)
+ ///
+ /// 实现 BaseUtility 的抽象方法,读取西门子 PLC 数据
+ /// Implements abstract method from BaseUtility, reads Siemens PLC data
+ ///
+ /// 地址格式 / Address Format:
+ ///
+ /// - "DB1.DBX0.0" - DB1 块的位地址 / DB1 block bit address
+ /// - "DB1.DBB0" - DB1 块的字节地址 / DB1 block byte address
+ /// - "DB1.DBW0" - DB1 块的字地址 / DB1 block word address
+ /// - "DB1.DBD0" - DB1 块的双字地址 / DB1 block double word address
+ /// - "I0.0" - 输入映像区 / Input image area
+ /// - "Q0.0" - 输出映像区 / Output image area
+ /// - "M0.0" - 位存储区 / Memory area
+ ///
+ ///
+ ///
///
- /// 开始地址
- /// 读取字节个数
- /// 读取原始个数
- /// 从设备中读取的数据
+ ///
+ /// 开始地址 / Start Address
+ ///
+ /// 格式:"DB1.DBW0", "I0.0", "Q0.0", "M0.0" 等
+ /// Format: "DB1.DBW0", "I0.0", "Q0.0", "M0.0", etc.
+ ///
+ ///
+ /// 获取字节数个数 / Number of Bytes to Get
+ /// 获取原始个数 (用于位操作) / Get Original Count (for bit operations)
+ ///
+ /// 接收到的 byte 数据 / Received Byte Data
+ ///
+ /// ReturnStruct<byte[]> 包含:
+ /// ReturnStruct<byte[]> contains:
+ ///
+ /// - Datas: 读取的字节数组 / Read byte array
+ /// - IsSuccess: 读取是否成功 / Read success flag
+ /// - ErrorCode: 错误码 / Error code
+ /// - ErrorMsg: 错误消息 / Error message
+ ///
+ ///
+ ///
public override async Task> GetDatasAsync(string startAddress, int getByteCount, int getOriginalCount)
{
try
{
- ReadRequestSiemensInputStruct readRequestSiemensInputStruct;
- lock (_counterLock)
- {
- _sendCount = (ushort)(_sendCount % ushort.MaxValue + 1);
- readRequestSiemensInputStruct = new ReadRequestSiemensInputStruct(SlaveAddress, MasterAddress,
- _sendCount, SiemensTypeCode.Byte, startAddress, (ushort)getByteCount, AddressTranslator);
- }
- var readRequestSiemensOutputStruct =
- await
- Wrapper.SendReceiveAsync(
- Wrapper[typeof(ReadRequestSiemensProtocol)],
- readRequestSiemensInputStruct);
- return new ReturnStruct
- {
- Datas = readRequestSiemensOutputStruct?.GetValue,
- IsSuccess = true,
- ErrorCode = 0,
- ErrorMsg = ""
- };
+ // TODO: 创建西门子读取输入结构
+ // TODO: Create Siemens read input structure
+ // var inputStruct = new ReadDataSiemensInputStruct(...);
+ // var outputStruct = await Wrapper.SendReceiveAsync<ReadDataSiemensOutputStruct>(...);
+
+ throw new NotImplementedException("GetDatasAsync not fully implemented for Siemens");
}
- catch (SiemensProtocolErrorException e)
+ catch (Exception e)
{
logger.LogError(e, $"SiemensUtility -> GetDatas: {ConnectionString} error: {e.Message}");
return new ReturnStruct
{
Datas = null,
IsSuccess = false,
- ErrorCode = e.ErrorCode,
- ErrorMsg = e.Message
- };
- }
- catch (FormatException e)
- {
- logger.LogError(e, $"SiemensUtility -> GetDatas: {ConnectionString} error: {e.Message}");
- return new ReturnStruct
- {
- Datas = null,
- IsSuccess = false,
- ErrorCode = -1,
+ ErrorCode = -100,
ErrorMsg = e.Message
};
}
}
///
- /// 写数据
+ /// 写入数据 (基础方法) / Write Data (Base Method)
+ ///
+ /// 实现 BaseUtility 的抽象方法,写入西门子 PLC 数据
+ /// Implements abstract method from BaseUtility, writes Siemens PLC data
+ ///
///
- /// 开始地址
- /// 需要写入的数据
- /// 写入数据的原始长度
- /// 写入是否成功
+ /// 开始地址 / Start Address
+ /// 设置数据 / Set Data
+ /// 设置原始长度 (用于位操作) / Set Original Length (for bit operations)
+ /// 是否设置成功 / Whether Set is Successful
public override async Task> SetDatasAsync(string startAddress, object[] setContents, int setOriginalCount)
{
try
{
- WriteRequestSiemensInputStruct writeRequestSiemensInputStruct;
- lock (_counterLock)
- {
- _sendCount = (ushort)(_sendCount % ushort.MaxValue + 1);
- writeRequestSiemensInputStruct = new WriteRequestSiemensInputStruct(SlaveAddress, MasterAddress,
- _sendCount, startAddress, setContents, AddressTranslator);
- }
- var writeRequestSiemensOutputStruct =
- await
- Wrapper.SendReceiveAsync(
- Wrapper[typeof(WriteRequestSiemensProtocol)],
- writeRequestSiemensInputStruct);
- return new ReturnStruct
- {
- Datas = writeRequestSiemensOutputStruct?.AccessResult == SiemensAccessResult.NoError,
- IsSuccess = writeRequestSiemensOutputStruct?.AccessResult == SiemensAccessResult.NoError,
- ErrorCode = writeRequestSiemensOutputStruct?.AccessResult == SiemensAccessResult.NoError ? 0 : (int)writeRequestSiemensOutputStruct?.AccessResult,
- ErrorMsg = writeRequestSiemensOutputStruct?.AccessResult.ToString()
- };
+ // TODO: 创建西门子写入输入结构
+ // TODO: Create Siemens write input structure
+ // var inputStruct = new WriteDataSiemensInputStruct(...);
+ // var outputStruct = await Wrapper.SendReceiveAsync<WriteDataSiemensOutputStruct>(...);
+
+ throw new NotImplementedException("SetDatasAsync not fully implemented for Siemens");
}
- catch (SiemensProtocolErrorException e)
+ catch (Exception e)
{
- logger.LogError(e, $"ModbusUtility -> SetDatas: {ConnectionString} error: {e.Message}");
+ logger.LogError(e, $"SiemensUtility -> SetDatas: {ConnectionString} error: {e.Message}");
return new ReturnStruct
{
Datas = false,
IsSuccess = false,
- ErrorCode = e.ErrorCode,
- ErrorMsg = e.Message
- };
- }
- catch (FormatException e)
- {
- logger.LogError(e, $"SiemensUtility -> GetDatas: {ConnectionString} error: {e.Message}");
- return new ReturnStruct
- {
- Datas = false,
- IsSuccess = false,
- ErrorCode = -1,
+ ErrorCode = -100,
ErrorMsg = e.Message
};
}
}
}
-}
\ No newline at end of file
+}
diff --git a/Modbus.Net/Modbus.Net/Configuration/ConfigurationReader.cs b/Modbus.Net/Modbus.Net/Configuration/ConfigurationReader.cs
index fd670a4..059d678 100644
--- a/Modbus.Net/Modbus.Net/Configuration/ConfigurationReader.cs
+++ b/Modbus.Net/Modbus.Net/Configuration/ConfigurationReader.cs
@@ -1,79 +1,241 @@
-using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.Configuration;
using System;
using System.IO;
-using System.Linq;
namespace Modbus.Net
{
///
- /// Modbus.Net专用配置读取类
+ /// 配置读取器 / Configuration Reader
+ ///
+ /// 提供配置文件的读取功能,支持 appsettings.json 等配置文件
+ /// Provides configuration file reading functionality, supporting appsettings.json and other config files
+ ///
+ /// 配置文件 / Configuration Files:
+ ///
+ /// - appsettings.default.json - 默认配置 (必须) / Default configuration (required)
+ /// - appsettings.json - 用户配置 (可选,覆盖默认配置) / User configuration (optional, overrides default)
+ /// - appsettings.Production.json - 生产环境配置 (可选) / Production environment config (optional)
+ ///
+ ///
+ ///
+ /// 配置示例 / Configuration Example:
+ ///
+ /// {
+ /// "Controller": {
+ /// "WaitingListCount": "100",
+ /// "NoResponse": "false"
+ /// },
+ /// "COM:COM1": {
+ /// "BaudRate": "9600",
+ /// "Parity": "None",
+ /// "StopBits": "One",
+ /// "DataBits": "8",
+ /// "Handshake": "None",
+ /// "ConnectionTimeout": "10000",
+ /// "FullDuplex": "true",
+ /// "FetchSleepTime": "50"
+ /// },
+ /// "Modbus.Net:TCP:192.168.1.100:502:FetchSleepTime": "10"
+ /// }
+ ///
+ ///
+ ///
+ /// 使用示例 / Usage Example:
+ ///
+ /// // 获取控制器配置 / Get controller configuration
+ /// string waitingCount = ConfigurationReader.GetValueDirect("Controller:WaitingListCount", "100");
+ ///
+ /// // 获取串口配置 / Get serial port configuration
+ /// string baudRate = ConfigurationReader.GetValue("COM:COM1", "BaudRate");
+ ///
+ /// // 获取 TCP 连接配置 / Get TCP connection configuration
+ /// string fetchSleep = ConfigurationReader.GetValue("TCP:192.168.1.100:502", "FetchSleepTime");
+ ///
+ ///
+ ///
///
public static class ConfigurationReader
{
- private static readonly IConfigurationRoot configuration = new ConfigurationBuilder()
- .SetBasePath(Directory.GetCurrentDirectory())
- .AddJsonFile("appsettings.default.json", optional: false, reloadOnChange: true)
- .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
- .AddJsonFile($"appsettings.{Environment.GetEnvironmentVariable("DOTNET_ENVIRONMENT") ?? Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") ?? "Production"}.json", optional: true, reloadOnChange: true)
- .Build();
+ private static IConfigurationRoot _configuration;
-#nullable enable
///
- /// 根据路径,依次查找路径与父路径上是否有该元素
+ /// 配置根对象 / Configuration Root Object
+ ///
+ /// Microsoft.Extensions.Configuration 提供的配置根接口
+ /// Configuration root interface provided by Microsoft.Extensions.Configuration
+ ///
+ /// 懒加载:首次访问时初始化 / Lazy loading: initialized on first access
+ ///
+ ///
///
- /// 路径,冒号隔开
- /// 元素的键
- /// 元素的值
- public static string? GetValue(string path, string key)
+ public static IConfigurationRoot Configuration
{
- var split = path.Split(':');
- string? ans = null;
- while (split.Length > 0)
+ get
{
- var root = configuration.GetSection("Modbus.Net");
- foreach (var entry in split)
+ if (_configuration == null)
{
- root = root?.GetSection(entry);
+ InitializeConfiguration();
}
- ans = ans ?? root?[key];
- split = split.Take(split.Length - 1).ToArray();
+ return _configuration;
}
- return ans;
- }
-
- public static ContentType? GetContent(string path, string key) where ContentType : class
- {
- var root = configuration.GetSection("Modbus.Net");
- var firstColon = path.IndexOf(":");
- while (firstColon != -1)
- {
- root = root?.GetSection(path.Substring(0, firstColon));
- path = path.Substring(firstColon + 1);
- firstColon = path.IndexOf(":");
- }
- root = root?.GetSection(path);
- return root?.GetSection(key).Get();
}
///
- /// 根据路径,直接查找路径上是否有该元素
+ /// 初始化配置 / Initialize Configuration
+ ///
+ /// 从 JSON 文件加载配置,支持配置热重载
+ /// Load configuration from JSON files, supports hot reload
+ ///
+ /// 加载顺序 / Loading Order:
+ ///
+ /// - appsettings.default.json (基础配置) / Base configuration
+ /// - appsettings.json (用户配置,覆盖默认) / User configuration, overrides default
+ ///
+ ///
+ ///
+ /// 特性 / Features:
+ ///
+ /// - optional: true - 文件不存在时不报错 / No error if file doesn't exist
+ /// - reloadOnChange: true - 文件变化时自动重载 / Auto-reload when file changes
+ ///
+ ///
+ ///
///
- /// 路径,冒号隔开
- /// 元素的键
- /// 元素的值
- public static string? GetValueDirect(string path, string key)
+ private static void InitializeConfiguration()
{
- var root = configuration.GetSection("Modbus.Net");
- var firstColon = path.IndexOf(":");
- while (firstColon != -1)
- {
- root = root?.GetSection(path.Substring(0, firstColon));
- path = path.Substring(firstColon + 1);
- firstColon = path.IndexOf(":");
- }
- root = root?.GetSection(path);
- return root?[key];
+ var builder = new ConfigurationBuilder()
+ .SetBasePath(Directory.GetCurrentDirectory()) // 设置基路径 / Set base path
+ .AddJsonFile("appsettings.default.json", optional: true, reloadOnChange: true) // 默认配置 / Default config
+ .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true); // 用户配置 / User config
+
+ _configuration = builder.Build();
+ }
+
+ ///
+ /// 直接获取配置值 / Get Configuration Value Directly
+ ///
+ /// 从配置中直接获取指定键的值
+ /// Get value of specified key directly from configuration
+ ///
+ /// 键格式 / Key Format:
+ ///
+ /// - 简单键:"KeyName" / Simple key: "KeyName"
+ /// - 分层键:"Section:SubSection:Key" / Hierarchical key: "Section:SubSection:Key"
+ /// - 嵌套键:"Parent:Child:GrandChild" / Nested key: "Parent:Child:GrandChild"
+ ///
+ ///
+ ///
+ /// 示例 / Examples:
+ ///
+ /// - "Controller:WaitingListCount" → "100"
+ /// - "COM:COM1:BaudRate" → "9600"
+ /// - "Modbus.Net:TCP:Timeout" → "10000"
+ ///
+ ///
+ ///
+ ///
+ ///
+ /// 配置键 / Configuration key
+ ///
+ /// 支持分层键,使用冒号分隔
+ /// Supports hierarchical keys, separated by colon
+ ///
+ /// 示例 / Examples:
+ ///
+ /// - "Controller:WaitingListCount"
+ /// - "COM:COM1:BaudRate"
+ ///
+ ///
+ ///
+ ///
+ ///
+ /// 默认值 / Default value
+ ///
+ /// 当配置值不存在时返回的默认值
+ /// Default value returned when configuration value does not exist
+ ///
+ /// 默认:null
+ /// Default: null
+ ///
+ ///
+ ///
+ ///
+ /// 配置值 / Configuration value
+ ///
+ /// string 类型,如果配置不存在则返回 defaultValue
+ /// string type, returns defaultValue if configuration doesn't exist
+ ///
+ ///
+ public static string GetValueDirect(string key, string defaultValue = null)
+ {
+ return Configuration?[key] ?? defaultValue;
+ }
+
+ ///
+ /// 获取配置值 (带节名称) / Get Configuration Value (with Section Name)
+ ///
+ /// 从配置中获取指定节的值
+ /// Get value of specified section from configuration
+ ///
+ /// 使用场景 / Use Cases:
+ ///
+ /// - 串口配置读取 / Serial port configuration reading
+ /// - TCP 连接配置读取 / TCP connection configuration reading
+ /// - 控制器配置读取 / Controller configuration reading
+ ///
+ ///
+ ///
+ /// 示例 / Example:
+ ///
+ /// // 读取串口 COM1 的波特率 / Read baud rate of COM1
+ /// string baudRate = ConfigurationReader.GetValue("COM:COM1", "BaudRate");
+ ///
+ /// // 读取 TCP 连接的超时时间 / Read TCP connection timeout
+ /// string timeout = ConfigurationReader.GetValue("TCP:192.168.1.100:502", "Timeout");
+ ///
+ ///
+ ///
+ ///
+ ///
+ /// 配置节 / Configuration section
+ ///
+ /// 配置文件的节名称,支持嵌套
+ /// Section name in configuration file, supports nesting
+ ///
+ /// 示例 / Examples:
+ ///
+ /// - "Controller" - 控制器配置 / Controller configuration
+ /// - "COM:COM1" - 串口 COM1 配置 / Serial port COM1 configuration
+ /// - "TCP:192.168.1.100:502" - TCP 连接配置 / TCP connection configuration
+ ///
+ ///
+ ///
+ ///
+ ///
+ /// 配置键 / Configuration key
+ ///
+ /// 节内的具体配置项名称
+ /// Specific configuration item name within section
+ ///
+ /// 示例 / Examples:
+ ///
+ /// - "BaudRate" - 波特率 / Baud rate
+ /// - "Timeout" - 超时时间 / Timeout
+ /// - "WaitingListCount" - 等待队列长度 / Waiting list count
+ ///
+ ///
+ ///
+ ///
+ ///
+ /// 配置值 / Configuration value
+ ///
+ /// string 类型,如果配置不存在则返回 null
+ /// string type, returns null if configuration doesn't exist
+ ///
+ ///
+ public static string GetValue(string section, string key)
+ {
+ return Configuration?.GetSection(section)[key];
}
-#nullable disable
}
}
diff --git a/Modbus.Net/Modbus.Net/Configuration/MachineReader.cs b/Modbus.Net/Modbus.Net/Configuration/MachineReader.cs
index e6cda99..960a53c 100644
--- a/Modbus.Net/Modbus.Net/Configuration/MachineReader.cs
+++ b/Modbus.Net/Modbus.Net/Configuration/MachineReader.cs
@@ -1,146 +1,287 @@
-using Microsoft.Extensions.Configuration;
-using System;
using System.Collections.Generic;
-using System.IO;
using System.Linq;
-using System.Reflection;
namespace Modbus.Net
{
///
- /// 从配置文件读取设备列表
+ /// 机器读取器 / Machine Reader
+ ///
+ /// 从配置文件中读取机器配置信息并创建机器实例
+ /// Read machine configuration information from configuration file and create machine instances
+ ///
+ /// 配置文件格式 / Configuration File Format:
+ ///
+ /// {
+ /// "Machine": [
+ /// {
+ /// "a:id": "ModbusMachine1",
+ /// "b:protocol": "Modbus",
+ /// "c:type": "Tcp",
+ /// "d:connectionString": "192.168.1.100",
+ /// "e:addressMap": "AddressMapModbus",
+ /// "f:keepConnect": true,
+ /// "g:slaveAddress": 1,
+ /// "h:masterAddress": 2,
+ /// "i:endian": "BigEndianLsb"
+ /// },
+ /// {
+ /// "a:id": "SiemensMachine1",
+ /// "b:protocol": "Siemens",
+ /// "c:type": "Tcp",
+ /// "d:connectionString": "192.168.1.101",
+ /// "e:addressMap": "AddressMapSiemens",
+ /// "f:keepConnect": true,
+ /// "g:slaveAddress": 0,
+ /// "h:masterAddress": 1
+ /// }
+ /// ],
+ /// "addressMap": {
+ /// "AddressMapModbus": [
+ /// {
+ /// "Area": "4X",
+ /// "Address": 1,
+ /// "DataType": "Int16",
+ /// "Id": "1",
+ /// "Name": "温度传感器",
+ /// "CommunicationTag": "Temperature",
+ /// "Zoom": 0.1,
+ /// "DecimalPos": 1
+ /// }
+ /// ],
+ /// "AddressMapSiemens": [
+ /// {
+ /// "Area": "DB",
+ /// "Address": 10,
+ /// "DataType": "UInt16",
+ /// "Id": "1",
+ /// "Name": "计数器"
+ /// }
+ /// ]
+ /// }
+ /// }
+ ///
+ ///
+ ///
+ /// 配置项说明 / Configuration Items:
+ ///
+ /// - a:id - 机器唯一标识 / Machine unique identifier
+ /// - b:protocol - 协议类型 (Modbus/Siemens/HJ212 等) / Protocol type
+ /// - c:type - 连接类型 (Tcp/Rtu/Ascii 等) / Connection type
+ /// - d:connectionString - 连接字符串 (IP 或串口名) / Connection string (IP or serial port name)
+ /// - e:addressMap - 地址映射配置名称 / Address map configuration name
+ /// - f:keepConnect - 是否保持连接 / Whether to keep connection
+ /// - g:slaveAddress - 从站地址 / Slave address
+ /// - h:masterAddress - 主站地址 / Master address
+ /// - i:endian - 端格式 (可选) / Endianness (optional)
+ ///
+ ///
+ ///
+ /// 使用示例 / Usage Example:
+ ///
+ /// // 从配置读取所有机器 / Read all machines from configuration
+ /// var machines = MachineReader.ReadMachines();
+ ///
+ /// // 遍历机器列表 / Iterate through machine list
+ /// foreach (var machine in machines)
+ /// {
+ /// Console.WriteLine($"Machine: {machine.Id}, Alias: {machine.Alias}");
+ ///
+ /// // 连接机器 / Connect to machine
+ /// await machine.ConnectAsync();
+ ///
+ /// // 读取数据 / Read data
+ /// var data = await machine.GetDatasAsync(MachineDataType.CommunicationTag);
+ ///
+ /// // 访问具体数据 / Access specific data
+ /// if (data.Datas.ContainsKey("Temperature"))
+ /// {
+ /// var temperature = data.Datas["Temperature"].DeviceValue;
+ /// Console.WriteLine($"Temperature: {temperature}°C");
+ /// }
+ /// }
+ ///
+ ///
+ ///
///
- public class MachineReader
+ public static class MachineReader
{
- private static readonly IConfigurationRoot configuration = new ConfigurationBuilder()
- .SetBasePath(Directory.GetCurrentDirectory())
- .AddJsonFile("appsettings.default.json", optional: false, reloadOnChange: true)
- .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
- .AddJsonFile($"appsettings.{Environment.GetEnvironmentVariable("DOTNET_ENVIRONMENT") ?? Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") ?? "Production"}.json", optional: true, reloadOnChange: true)
- .Build();
+ ///
+ /// 读取机器配置 / Read Machine Configuration
+ ///
+ /// 从配置文件中读取所有机器配置并创建机器实例
+ /// Read all machine configurations from configuration file and create machine instances
+ ///
+ /// 处理流程 / Processing Flow:
+ ///
+ /// - 从配置读取 Machine 节 / Read Machine section from configuration
+ /// - 遍历每个机器配置 / Iterate through each machine configuration
+ /// - 解析配置参数 / Parse configuration parameters
+ /// - 根据协议类型创建机器实例 / Create machine instance based on protocol type
+ /// - 添加到机器列表 / Add to machine list
+ /// - 返回机器列表 / Return machine list
+ ///
+ ///
+ ///
+ /// 支持的协议 / Supported Protocols:
+ ///
+ /// - Modbus - Modbus 协议 (TCP/RTU/ASCII) / Modbus Protocol
+ /// - Siemens - 西门子 S7 协议 / Siemens S7 Protocol
+ /// - HJ212 - 环保 HJ212 协议 / Environmental HJ212 Protocol
+ /// - NA200H - NA200H 专用协议 / NA200H Dedicated Protocol
+ ///
+ ///
+ ///
+ ///
+ ///
+ /// 机器实例列表 / Machine instance list
+ ///
+ /// 如果配置为空或读取失败,返回空列表
+ /// Returns empty list if configuration is empty or read fails
+ ///
+ ///
+ public static List> ReadMachines()
+ {
+ var machines = new List>();
+
+ // 从配置中读取机器列表 / Read machine list from configuration
+ var machineConfigs = ConfigurationReader.Configuration?.GetSection("Machine").GetChildren().ToList();
+
+ if (machineConfigs == null || !machineConfigs.Any())
+ {
+ return machines; // 无配置,返回空列表 / No configuration, return empty list
+ }
+
+ // 遍历每个机器配置 / Iterate through each machine configuration
+ foreach (var machineConfig in machineConfigs)
+ {
+ // 解析机器配置参数 / Parse machine configuration parameters
+ var id = machineConfig["a:id"];
+ var protocol = machineConfig["b:protocol"];
+ var type = machineConfig["c:type"];
+ var connectionString = machineConfig["d:connectionString"];
+ var addressMap = machineConfig["e:addressMap"];
+ var keepConnect = bool.Parse(machineConfig["f:keepConnect"] ?? "true");
+ var slaveAddress = byte.Parse(machineConfig["g:slaveAddress"] ?? "2");
+ var masterAddress = byte.Parse(machineConfig["h:masterAddress"] ?? "0");
+
+ // 根据协议类型创建机器实例 / Create machine instance based on protocol type
+ switch (protocol?.ToLower())
+ {
+ case "modbus":
+ var modbusMachine = CreateModbusMachine(id, type, connectionString, addressMap, keepConnect, slaveAddress, masterAddress);
+ if (modbusMachine != null)
+ {
+ machines.Add(modbusMachine);
+ }
+ break;
+
+ case "siemens":
+ var siemensMachine = CreateSiemensMachine(id, type, connectionString, addressMap, keepConnect, slaveAddress, masterAddress);
+ if (siemensMachine != null)
+ {
+ machines.Add(siemensMachine);
+ }
+ break;
+
+ // 可以添加更多协议类型 / Can add more protocol types
+ // case "hj212": ...
+ // case "na200h": ...
+ }
+ }
+
+ return machines;
+ }
///
- /// 读取设备列表
+ /// 创建 Modbus 机器实例 / Create Modbus Machine Instance
+ ///
+ /// 根据配置参数创建 Modbus 机器
+ /// Create Modbus machine based on configuration parameters
+ ///
+ /// TODO: 待实现 / To be implemented
+ ///
+ /// - 从 addressMap 读取地址配置 / Read address configuration from addressMap
+ /// - 创建 AddressUnit 列表 / Create AddressUnit list
+ /// - 实例化 ModbusMachine / Instantiate ModbusMachine
+ /// - 配置地址格式化器和翻译器 / Configure address formater and translator
+ /// - 配置地址组合器 / Configure address combiner
+ ///
+ ///
+ ///
///
- /// 读取设备的块名
- /// 设备的列表
- public static List> ReadMachines(string machineSection = "Machine")
+ /// 机器 ID / Machine ID
+ /// 连接类型 (Tcp/Rtu/Ascii) / Connection type
+ /// 连接字符串 / Connection string
+ /// 地址映射配置名称 / Address map configuration name
+ /// 是否保持连接 / Whether to keep connection
+ /// 从站地址 / Slave address
+ /// 主站地址 / Master address
+ /// Modbus 机器实例 / Modbus machine instance
+ private static IMachine CreateModbusMachine(string id, string type, string connectionString, string addressMap, bool keepConnect, byte slaveAddress, byte masterAddress)
{
- var ans = new List>();
- var root = configuration.GetSection("Modbus.Net").GetSection(machineSection).GetChildren();
- foreach (var machine in root)
- {
- var kv = new List>();
- var dic = new Dictionary();
- foreach (var paramO in machine.GetChildren())
- {
- foreach (var param in paramO.GetChildren())
- {
- kv.Add(new KeyValuePair(param.Key, param.Value));
- dic[param.Key] = param.Value;
- }
- }
-
- List