{"id":40580,"date":"2026-03-01T18:24:08","date_gmt":"2026-03-01T10:24:08","guid":{"rendered":"https:\/\/www.modbus.cn\/40580.html"},"modified":"2026-03-01T21:06:26","modified_gmt":"2026-03-01T13:06:26","slug":"c-gong-ye-zi-dong-hua-kai-fa-wan-quan-zhi-nan-gou-jian-qi-ye-ji-ying-yong","status":"publish","type":"post","link":"https:\/\/www.modbus.cn\/en\/40580.html","title":{"rendered":"C# \u5de5\u4e1a\u81ea\u52a8\u5316\u5f00\u53d1\u5b8c\u5168\u6307\u5357\uff1a\u6784\u5efa\u4f01\u4e1a\u7ea7\u5e94\u7528"},"content":{"rendered":"<h1>C#\/.NET\u5de5\u4e1a\u81ea\u52a8\u5316\u5f00\u53d1\u5b8c\u5168\u6307\u5357\uff1a\u6784\u5efa\u4f01\u4e1a\u7ea7\u5de5\u4e1a\u5e94\u7528<\/h1>\n<h2>\u5f15\u8a00\uff1aC#\/.NET\u5728\u5de5\u4e1a\u81ea\u52a8\u5316\u4e2d\u7684\u4f18\u52bf<\/h2>\n<p>C#\/.NET\u5e73\u53f0\u51ed\u501f\u5176\u5f3a\u5927\u7684\u7c7b\u578b\u7cfb\u7edf\u3001\u4e30\u5bcc\u7684\u7c7b\u5e93\u3001\u5353\u8d8a\u7684\u6027\u80fd\u548c\u5fae\u8f6f\u7684\u5168\u9762\u652f\u6301\uff0c\u5728\u4f01\u4e1a\u7ea7\u5de5\u4e1a\u81ea\u52a8\u5316\u9886\u57df\u5360\u636e\u91cd\u8981\u5730\u4f4d\u3002\u4ece\u4f20\u7edf\u7684Windows\u684c\u9762\u5e94\u7528\u5230\u73b0\u4ee3\u7684\u4e91\u539f\u751f\u5fae\u670d\u52a1\uff0cC#\/.NET\u63d0\u4f9b\u4e86\u5b8c\u6574\u7684\u89e3\u51b3\u65b9\u6848\u6808\uff0c\u7279\u522b\u9002\u5408\u9700\u8981\u9ad8\u53ef\u9760\u6027\u3001\u9ad8\u6027\u80fd\u548c\u957f\u671f\u7ef4\u62a4\u7684\u5de5\u4e1a\u5e94\u7528\u3002<\/p>\n<p>\u672c\u6587\u5c06\u6df1\u5165\u63a2\u8ba8C#\/.NET\u5728\u5de5\u4e1a\u81ea\u52a8\u5316\u4e2d\u7684\u5e94\u7528\uff0c\u91cd\u70b9\u4ecb\u7ecdNModbus\u5e93\u7684\u4f7f\u7528\u3001WPF\/WinForms\u754c\u9762\u5f00\u53d1\u3001\u4f01\u4e1a\u7ea7\u67b6\u6784\u8bbe\u8ba1\uff0c\u5e76\u7ed3\u5408\u5b9e\u9645\u6848\u4f8b\u5c55\u793a\u5982\u4f55\u6784\u5efa\u4e13\u4e1a\u7ea7\u7684\u5de5\u4e1a\u81ea\u52a8\u5316\u7cfb\u7edf\u3002<\/p>\n<h2>\u4e00\u3001C#\/.NET\u5de5\u4e1a\u5f00\u53d1\u73af\u5883\u914d\u7f6e<\/h2>\n<h3>1.1 \u5f00\u53d1\u73af\u5883\u8981\u6c42<\/h3>\n<pre><code class=\"language-xml\">&lt;!-- .NET SDK\u7248\u672c\u8981\u6c42 --&gt;\n&lt;Project Sdk=\"Microsoft.NET.Sdk\"&gt;\n  &lt;PropertyGroup&gt;\n    &lt;TargetFramework&gt;net8.0&lt;\/TargetFramework&gt;\n    &lt;OutputType&gt;Exe&lt;\/OutputType&gt;\n    &lt;Nullable&gt;enable&lt;\/Nullable&gt;\n  &lt;\/PropertyGroup&gt;\n&lt;\/Project&gt;\n<\/code><\/pre>\n<h3>1.2 \u9879\u76ee\u7ed3\u6784\u548c\u4f9d\u8d56\u914d\u7f6e<\/h3>\n<pre><code class=\"language-xml\">&lt;!-- IndustrialAutomation.csproj --&gt;\n&lt;Project Sdk=\"Microsoft.NET.Sdk\"&gt;\n\n  &lt;PropertyGroup&gt;\n    &lt;OutputType&gt;WinExe&lt;\/OutputType&gt;\n    &lt;TargetFramework&gt;net8.0-windows&lt;\/TargetFramework&gt;\n    &lt;UseWPF&gt;true&lt;\/UseWPF&gt;\n    &lt;Nullable&gt;enable&lt;\/Nullable&gt;\n  &lt;\/PropertyGroup&gt;\n\n  &lt;ItemGroup&gt;\n    &lt;!-- Modbus\u901a\u4fe1 --&gt;\n    &lt;PackageReference Include=\"NModbus\" Version=\"3.0.73\" \/&gt;\n    &lt;PackageReference Include=\"NModbus.Serial\" Version=\"3.0.73\" \/&gt;\n\n    &lt;!-- \u4e32\u53e3\u901a\u4fe1 --&gt;\n    &lt;PackageReference Include=\"System.IO.Ports\" Version=\"8.0.0\" \/&gt;\n\n    &lt;!-- \u65e5\u5fd7\u8bb0\u5f55 --&gt;\n    &lt;PackageReference Include=\"Serilog\" Version=\"3.1.1\" \/&gt;\n    &lt;PackageReference Include=\"Serilog.Sinks.File\" Version=\"5.0.0\" \/&gt;\n    &lt;PackageReference Include=\"Serilog.Sinks.Console\" Version=\"5.0.0\" \/&gt;\n\n    &lt;!-- \u4f9d\u8d56\u6ce8\u5165 --&gt;\n    &lt;PackageReference Include=\"Microsoft.Extensions.DependencyInjection\" Version=\"8.0.0\" \/&gt;\n    &lt;PackageReference Include=\"Microsoft.Extensions.Hosting\" Version=\"8.0.0\" \/&gt;\n\n    &lt;!-- \u914d\u7f6e\u7ba1\u7406 --&gt;\n    &lt;PackageReference Include=\"Microsoft.Extensions.Configuration\" Version=\"8.0.0\" \/&gt;\n    &lt;PackageReference Include=\"Microsoft.Extensions.Configuration.Json\" Version=\"8.0.0\" \/&gt;\n\n    &lt;!-- \u6570\u636e\u9a8c\u8bc1 --&gt;\n    &lt;PackageReference Include=\"FluentValidation\" Version=\"11.8.0\" \/&gt;\n\n    &lt;!-- \u5355\u5143\u6d4b\u8bd5 --&gt;\n    &lt;PackageReference Include=\"xunit\" Version=\"2.6.3\" \/&gt;\n    &lt;PackageReference Include=\"xunit.runner.visualstudio\" Version=\"2.5.5\" \/&gt;\n    &lt;PackageReference Include=\"Moq\" Version=\"4.20.69\" \/&gt;\n  &lt;\/ItemGroup&gt;\n\n&lt;\/Project&gt;\n<\/code><\/pre>\n<h3>1.3 \u9879\u76ee\u76ee\u5f55\u7ed3\u6784<\/h3>\n<pre><code>IndustrialAutomation\/\n\u251c\u2500\u2500 src\/\n\u2502   \u251c\u2500\u2500 IndustrialAutomation.Core\/          # \u6838\u5fc3\u4e1a\u52a1\u903b\u8f91\n\u2502   \u2502   \u251c\u2500\u2500 Models\/                         # \u6570\u636e\u6a21\u578b\n\u2502   \u2502   \u251c\u2500\u2500 Services\/                       # \u4e1a\u52a1\u670d\u52a1\n\u2502   \u2502   \u251c\u2500\u2500 Interfaces\/                     # \u63a5\u53e3\u5b9a\u4e49\n\u2502   \u2502   \u2514\u2500\u2500 Exceptions\/                     # \u81ea\u5b9a\u4e49\u5f02\u5e38\n\u2502   \u2502\n\u2502   \u251c\u2500\u2500 IndustrialAutomation.Modbus\/        # Modbus\u901a\u4fe1\u6a21\u5757\n\u2502   \u2502   \u251c\u2500\u2500 Clients\/                        # Modbus\u5ba2\u6237\u7aef\n\u2502   \u2502   \u251c\u2500\u2500 Servers\/                        # Modbus\u670d\u52a1\u5668\n\u2502   \u2502   \u2514\u2500\u2500 Converters\/                     # \u6570\u636e\u8f6c\u6362\u5668\n\u2502   \u2502\n\u2502   \u251c\u2500\u2500 IndustrialAutomation.UI\/            # \u7528\u6237\u754c\u9762\n\u2502   \u2502   \u251c\u2500\u2500 Views\/                          # WPF\u89c6\u56fe\n\u2502   \u2502   \u251c\u2500\u2500 ViewModels\/                     # MVVM\u89c6\u56fe\u6a21\u578b\n\u2502   \u2502   \u251c\u2500\u2500 Converters\/                     # \u503c\u8f6c\u6362\u5668\n\u2502   \u2502   \u2514\u2500\u2500 Behaviors\/                      # \u884c\u4e3a\n\u2502   \u2502\n\u2502   \u2514\u2500\u2500 IndustrialAutomation.Host\/          # \u5e94\u7528\u7a0b\u5e8f\u5bbf\u4e3b\n\u2502\n\u251c\u2500\u2500 tests\/\n\u2502   \u251c\u2500\u2500 IndustrialAutomation.Core.Tests\/\n\u2502   \u251c\u2500\u2500 IndustrialAutomation.Modbus.Tests\/\n\u2502   \u2514\u2500\u2500 IndustrialAutomation.Integration.Tests\/\n\u2502\n\u251c\u2500\u2500 config\/\n\u2502   \u251c\u2500\u2500 appsettings.json                    # \u5e94\u7528\u914d\u7f6e\n\u2502   \u251c\u2500\u2500 appsettings.Development.json        # \u5f00\u53d1\u73af\u5883\u914d\u7f6e\n\u2502   \u2514\u2500\u2500 devices.json                        # \u8bbe\u5907\u914d\u7f6e\n\u2502\n\u2514\u2500\u2500 docs\/                                   # \u9879\u76ee\u6587\u6863\n<\/code><\/pre>\n<h2>\u4e8c\u3001NModbus\u5e93\u6df1\u5ea6\u4f7f\u7528<\/h2>\n<h3>2.1 \u57fa\u7840Modbus TCP\u5ba2\u6237\u7aef<\/h3>\n<pre><code class=\"language-csharp\">using NModbus;\nusing NModbus.Device;\nusing System.Net.Sockets;\n\nnamespace IndustrialAutomation.Modbus.Clients\n{\n    \/\/\/ &lt;summary&gt;\n    \/\/\/ Modbus TCP\u5ba2\u6237\u7aef\n    \/\/\/ &lt;\/summary&gt;\n    public class ModbusTcpClient : IModbusClient, IDisposable\n    {\n        private readonly ILogger&lt;ModbusTcpClient&gt; _logger;\n        private readonly TcpClient _tcpClient;\n        private IModbusMaster? _modbusMaster;\n        private bool _disposed;\n\n        \/\/\/ &lt;summary&gt;\n        \/\/\/ \u8bbe\u5907\u8fde\u63a5\u72b6\u6001\n        \/\/\/ &lt;\/summary&gt;\n        public bool IsConnected =&gt; _tcpClient?.Connected ?? false;\n\n        \/\/\/ &lt;summary&gt;\n        \/\/\/ \u8bbe\u5907IP\u5730\u5740\n        \/\/\/ &lt;\/summary&gt;\n        public string IpAddress { get; }\n\n        \/\/\/ &lt;summary&gt;\n        \/\/\/ \u8bbe\u5907\u7aef\u53e3\n        \/\/\/ &lt;\/summary&gt;\n        public int Port { get; }\n\n        \/\/\/ &lt;summary&gt;\n        \/\/\/ \u521d\u59cb\u5316Modbus TCP\u5ba2\u6237\u7aef\n        \/\/\/ &lt;\/summary&gt;\n        public ModbusTcpClient(\n            string ipAddress,\n            int port = 502,\n            ILogger&lt;ModbusTcpClient&gt;? logger = null)\n        {\n            IpAddress = ipAddress ?? throw new ArgumentNullException(nameof(ipAddress));\n            Port = port;\n            _logger = logger ?? NullLogger&lt;ModbusTcpClient&gt;.Instance;\n            _tcpClient = new TcpClient();\n        }\n\n        \/\/\/ &lt;summary&gt;\n        \/\/\/ \u8fde\u63a5\u5230\u8bbe\u5907\n        \/\/\/ &lt;\/summary&gt;\n        public async Task&lt;bool&gt; ConnectAsync(CancellationToken cancellationToken = default)\n        {\n            try\n            {\n                _logger.LogInformation(\"\u6b63\u5728\u8fde\u63a5\u5230\u8bbe\u5907 {IpAddress}:{Port}\", IpAddress, Port);\n\n                await _tcpClient.ConnectAsync(IpAddress, Port, cancellationToken);\n\n                var factory = new ModbusFactory();\n                _modbusMaster = factory.CreateMaster(_tcpClient);\n\n                \/\/ \u8bbe\u7f6e\u8d85\u65f6\u65f6\u95f4\n                _modbusMaster.Transport.ReadTimeout = 3000;\n                _modbusMaster.Transport.WriteTimeout = 3000;\n\n                _logger.LogInformation(\"\u6210\u529f\u8fde\u63a5\u5230\u8bbe\u5907 {IpAddress}:{Port}\", IpAddress, Port);\n                return true;\n            }\n            catch (Exception ex)\n            {\n                _logger.LogError(ex, \"\u8fde\u63a5\u8bbe\u5907 {IpAddress}:{Port} \u5931\u8d25\", IpAddress, Port);\n                return false;\n            }\n        }\n\n        \/\/\/ &lt;summary&gt;\n        \/\/\/ \u8bfb\u53d6\u4fdd\u6301\u5bc4\u5b58\u5668\n        \/\/\/ &lt;\/summary&gt;\n        public async Task&lt;ushort[]&gt; ReadHoldingRegistersAsync(\n            byte slaveAddress,\n            ushort startAddress,\n            ushort numberOfPoints,\n            CancellationToken cancellationToken = default)\n        {\n            ValidateConnection();\n\n            try\n            {\n                _logger.LogDebug(\n                    \"\u8bfb\u53d6\u4fdd\u6301\u5bc4\u5b58\u5668 - \u4ece\u7ad9: {SlaveAddress}, \u5730\u5740: {StartAddress}, \u6570\u91cf: {NumberOfPoints}\",\n                    slaveAddress, startAddress, numberOfPoints);\n\n                var result = await Task.Run(() =&gt;\n                    _modbusMaster!.ReadHoldingRegisters(slaveAddress, startAddress, numberOfPoints),\n                    cancellationToken);\n\n                _logger.LogDebug(\n                    \"\u8bfb\u53d6\u4fdd\u6301\u5bc4\u5b58\u5668\u6210\u529f - \u4ece\u7ad9: {SlaveAddress}, \u5730\u5740: {StartAddress}, \u6570\u636e: {Data}\",\n                    slaveAddress, startAddress, result);\n\n                return result;\n            }\n            catch (Exception ex)\n            {\n                _logger.LogError(ex,\n                    \"\u8bfb\u53d6\u4fdd\u6301\u5bc4\u5b58\u5668\u5931\u8d25 - \u4ece\u7ad9: {SlaveAddress}, \u5730\u5740: {StartAddress}, \u6570\u91cf: {NumberOfPoints}\",\n                    slaveAddress, startAddress, numberOfPoints);\n                throw new ModbusCommunicationException(\n                    $\"\u8bfb\u53d6\u4fdd\u6301\u5bc4\u5b58\u5668\u5931\u8d25: {ex.Message}\", ex);\n            }\n        }\n\n        \/\/\/ &lt;summary&gt;\n        \/\/\/ \u5199\u5165\u5355\u4e2a\u4fdd\u6301\u5bc4\u5b58\u5668\n        \/\/\/ &lt;\/summary&gt;\n        public async Task WriteSingleRegisterAsync(\n            byte slaveAddress,\n            ushort registerAddress,\n            ushort value,\n            CancellationToken cancellationToken = default)\n        {\n            ValidateConnection();\n\n            try\n            {\n                _logger.LogDebug(\n                    \"\u5199\u5165\u5355\u4e2a\u5bc4\u5b58\u5668 - \u4ece\u7ad9: {SlaveAddress}, \u5730\u5740: {RegisterAddress}, \u503c: {Value}\",\n                    slaveAddress, registerAddress, value);\n\n                await Task.Run(() =&gt;\n                    _modbusMaster!.WriteSingleRegister(slaveAddress, registerAddress, value),\n                    cancellationToken);\n\n                _logger.LogDebug(\n                    \"\u5199\u5165\u5355\u4e2a\u5bc4\u5b58\u5668\u6210\u529f - \u4ece\u7ad9: {SlaveAddress}, \u5730\u5740: {RegisterAddress}\",\n                    slaveAddress, registerAddress);\n            }\n            catch (Exception ex)\n            {\n                _logger.LogError(ex,\n                    \"\u5199\u5165\u5355\u4e2a\u5bc4\u5b58\u5668\u5931\u8d25 - \u4ece\u7ad9: {SlaveAddress}, \u5730\u5740: {RegisterAddress}, \u503c: {Value}\",\n                    slaveAddress, registerAddress, value);\n                throw new ModbusCommunicationException(\n                    $\"\u5199\u5165\u5355\u4e2a\u5bc4\u5b58\u5668\u5931\u8d25: {ex.Message}\", ex);\n            }\n        }\n\n        \/\/\/ &lt;summary&gt;\n        \/\/\/ \u5199\u5165\u591a\u4e2a\u4fdd\u6301\u5bc4\u5b58\u5668\n        \/\/\/ &lt;\/summary&gt;\n        public async Task WriteMultipleRegistersAsync(\n            byte slaveAddress,\n            ushort startAddress,\n            ushort[] data,\n            CancellationToken cancellationToken = default)\n        {\n            ValidateConnection();\n\n            try\n            {\n                _logger.LogDebug(\n                    \"\u5199\u5165\u591a\u4e2a\u5bc4\u5b58\u5668 - \u4ece\u7ad9: {SlaveAddress}, \u5730\u5740: {StartAddress}, \u6570\u636e\u957f\u5ea6: {Length}\",\n                    slaveAddress, startAddress, data.Length);\n\n                await Task.Run(() =&gt;\n                    _modbusMaster!.WriteMultipleRegisters(slaveAddress, startAddress, data),\n                    cancellationToken);\n\n                _logger.LogDebug(\n                    \"\u5199\u5165\u591a\u4e2a\u5bc4\u5b58\u5668\u6210\u529f - \u4ece\u7ad9: {SlaveAddress}, \u5730\u5740: {StartAddress}\",\n                    slaveAddress, startAddress);\n            }\n            catch (Exception ex)\n            {\n                _logger.LogError(ex,\n                    \"\u5199\u5165\u591a\u4e2a\u5bc4\u5b58\u5668\u5931\u8d25 - \u4ece\u7ad9: {SlaveAddress}, \u5730\u5740: {StartAddress}, \u6570\u636e\u957f\u5ea6: {Length}\",\n                    slaveAddress, startAddress, data.Length);\n                throw new ModbusCommunicationException(\n                    $\"\u5199\u5165\u591a\u4e2a\u5bc4\u5b58\u5668\u5931\u8d25: {ex.Message}\", ex);\n            }\n        }\n\n        \/\/\/ &lt;summary&gt;\n        \/\/\/ \u8bfb\u53d6\u7ebf\u5708\u72b6\u6001\n        \/\/\/ &lt;\/summary&gt;\n        public async Task&lt;bool[]&gt; ReadCoilsAsync(\n            byte slaveAddress,\n            ushort startAddress,\n            ushort numberOfPoints,\n            CancellationToken cancellationToken = default)\n        {\n            ValidateConnection();\n\n            try\n            {\n                _logger.LogDebug(\n                    \"\u8bfb\u53d6\u7ebf\u5708 - \u4ece\u7ad9: {SlaveAddress}, \u5730\u5740: {StartAddress}, \u6570\u91cf: {NumberOfPoints}\",\n                    slaveAddress, startAddress, numberOfPoints);\n\n                var result = await Task.Run(() =&gt;\n                    _modbusMaster!.ReadCoils(slaveAddress, startAddress, numberOfPoints),\n                    cancellationToken);\n\n                _logger.LogDebug(\n                    \"\u8bfb\u53d6\u7ebf\u5708\u6210\u529f - \u4ece\u7ad9: {SlaveAddress}, \u5730\u5740: {StartAddress}, \u6570\u636e: {Data}\",\n                    slaveAddress, startAddress, result);\n\n                return result;\n            }\n            catch (Exception ex)\n            {\n                _logger.LogError(ex,\n                    \"\u8bfb\u53d6\u7ebf\u5708\u5931\u8d25 - \u4ece\u7ad9: {SlaveAddress}, \u5730\u5740: {StartAddress}, \u6570\u91cf: {NumberOfPoints}\",\n                    slaveAddress, startAddress, numberOfPoints);\n                throw new ModbusCommunicationException(\n                    $\"\u8bfb\u53d6\u7ebf\u5708\u5931\u8d25: {ex.Message}\", ex);\n            }\n        }\n\n        \/\/\/ &lt;summary&gt;\n        \/\/\/ \u6279\u91cf\u8bfb\u53d6\u4f18\u5316\n        \/\/\/ &lt;\/summary&gt;\n        public async Task&lt;Dictionary&lt;ushort, ushort[]&gt;&gt; BatchReadRegistersAsync(\n            byte slaveAddress,\n            IEnumerable&lt;RegisterReadRequest&gt; requests,\n            CancellationToken cancellationToken = default)\n        {\n            ValidateConnection();\n\n            var results = new Dictionary&lt;ushort, ushort[]&gt;();\n\n            foreach (var request in requests)\n            {\n                try\n                {\n                    var data = await ReadHoldingRegistersAsync(\n                        slaveAddress,\n                        request.StartAddress,\n                        request.NumberOfPoints,\n                        cancellationToken);\n\n                    results[request.StartAddress] = data;\n                }\n                catch (Exception ex)\n                {\n                    _logger.LogWarning(ex,\n                        \"\u6279\u91cf\u8bfb\u53d6\u5931\u8d25 - \u5730\u5740: {StartAddress}, \u6570\u91cf: {NumberOfPoints}\",\n                        request.StartAddress, request.NumberOfPoints);\n                    results[request.StartAddress] = Array.Empty&lt;ushort&gt;();\n                }\n            }\n\n            return results;\n        }\n\n        \/\/\/ &lt;summary&gt;\n        \/\/\/ \u65ad\u5f00\u8fde\u63a5\n        \/\/\/ &lt;\/summary&gt;\n        public void Disconnect()\n        {\n            try\n            {\n                _modbusMaster?.Dispose();\n                _modbusMaster = null;\n\n                if (_tcpClient.Connected)\n                {\n                    _tcpClient.Close();\n                }\n\n                _logger.LogInformation(\"\u5df2\u65ad\u5f00\u4e0e\u8bbe\u5907 {IpAddress}:{Port} \u7684\u8fde\u63a5\", IpAddress, Port);\n            }\n            catch (Exception ex)\n            {\n                _logger.LogError(ex, \"\u65ad\u5f00\u8fde\u63a5\u65f6\u53d1\u751f\u9519\u8bef\");\n            }\n        }\n\n        \/\/\/ &lt;summary&gt;\n        \/\/\/ \u9a8c\u8bc1\u8fde\u63a5\u72b6\u6001\n        \/\/\/ &lt;\/summary&gt;\n        private void ValidateConnection()\n        {\n            if (!IsConnected || _modbusMaster == null)\n            {\n                throw new InvalidOperationException(\"\u8bbe\u5907\u672a\u8fde\u63a5\");\n            }\n        }\n\n        \/\/\/ &lt;summary&gt;\n        \/\/\/ \u91ca\u653e\u8d44\u6e90\n        \/\/\/ &lt;\/summary&gt;\n        public void Dispose()\n        {\n            Dispose(true);\n            GC.SuppressFinalize(this);\n        }\n\n        protected virtual void Dispose(bool disposing)\n        {\n            if (!_disposed)\n            {\n                if (disposing)\n                {\n                    Disconnect();\n                    _tcpClient.Dispose();\n                }\n\n                _disposed = true;\n            }\n        }\n    }\n\n    \/\/\/ &lt;summary&gt;\n    \/\/\/ \u5bc4\u5b58\u5668\u8bfb\u53d6\u8bf7\u6c42\n    \/\/\/ &lt;\/summary&gt;\n    public record RegisterReadRequest(\n        ushort StartAddress,\n        ushort NumberOfPoints);\n}\n<\/code><\/pre>\n<h3>2.2 Modbus RTU\u4e32\u53e3\u5ba2\u6237\u7aef<\/h3>\n<p>&#8220;`csharp<br \/>\nusing NModbus;<br \/>\nusing NModbus.Serial;<br \/>\nusing System.IO.Ports;<\/p>\n<p>namespace IndustrialAutomation.Modbus.Clients<br \/>\n{<br \/>\n    \/\/\/ <\/p>\n<summary>\n    \/\/\/ Modbus RTU\u4e32\u53e3\u5ba2\u6237\u7aef<br \/>\n    \/\/\/ <\/summary>\n<p>    public class ModbusRtuClient : IModbusClient, IDisposable<br \/>\n    {<br \/>\n        private readonly ILogger<ModbusRtuClient> _logger;<br \/>\n        private readonly SerialPort _serialPort;<br \/>\n        private IModbusMaster? _modbusMaster;<br \/>\n        private bool _disposed;<\/p>\n<pre><code>    \/\/\/ &lt;summary&gt;\n    \/\/\/ \u4e32\u53e3\u8fde\u63a5\u72b6\u6001\n    \/\/\/ &lt;\/summary&gt;\n    public bool IsConnected =&gt; _serialPort?.IsOpen ?? false;\n\n    \/\/\/ &lt;summary&gt;\n    \/\/\/ \u4e32\u53e3\u540d\u79f0\n    \/\/\/ &lt;\/summary&gt;\n    public string PortName { get; }\n\n    \/\/\/ &lt;summary&gt;\n    \/\/\/ \u6ce2\u7279\u7387\n    \/\/\/ &lt;\/summary&gt;\n    public int BaudRate { get; }\n\n    \/\/\/ &lt;summary&gt;\n    \/\/\/ \u521d\u59cb\u5316Modbus RTU\u5ba2\u6237\u7aef\n    \/\/\/ &lt;\/summary&gt;\n    public ModbusRtuClient(\n        string portName,\n        int baudRate = 9600,\n        ILogger&lt;ModbusRtuClient&gt;? logger = null)\n    {\n        PortName = portName ?? throw new ArgumentNullException(nameof(portName));\n        BaudRate = baudRate;\n        _logger = logger ?? NullLogger&lt;ModbusRtuClient&gt;.Instance;\n\n        _serialPort = new SerialPort(portName)\n        {\n            BaudRate = baudRate,\n            DataBits = 8,\n            Parity = Parity.None,\n            StopBits = StopBits.One,\n            ReadTimeout = 1000,\n            WriteTimeout = 1000\n        };\n    }\n\n    \/\/\/ &lt;summary&gt;\n    \/\/\/ \u8fde\u63a5\u5230\u4e32\u53e3\u8bbe\u5907\n    \/\/\/ &lt;\/summary&gt;\n    public async Task&lt;bool&gt; ConnectAsync(CancellationToken cancellationToken = default)\n    {\n        try\n        {\n            _logger.LogInformation(\"\u6b63\u5728\u6253\u5f00\u4e32\u53e3 {PortName} (\u6ce2\u7279\u7387: {BaudRate})\", PortName, BaudRate);\n\n            await Task.Run(() =&gt;\n            {\n                if (!_serialPort.IsOpen)\n                {\n                    _serialPort.Open();\n                }\n            }, cancellationToken);\n\n            var factory = new ModbusFactory();\n            var adapter = new SerialPortAdapter(_serialPort);\n            _modbusMaster = factory.CreateRtuMaster(adapter);\n\n            \/\/ \u914d\u7f6eModbus\u53c2\u6570\n            _modbusMaster.Transport.ReadTimeout = 1000;\n            _modbusMaster.Transport.WriteTimeout = 1000;\n            _modbusMaster.Transport.Retries = 3;\n            _modbusMaster.Transport.WaitToRetryMilliseconds = 100;\n\n            _logger.LogInformation(\"\u6210\u529f\u6253\u5f00\u4e32\u53e3 {PortName}\", PortName);\n            return true;\n        }\n        catch (Exception ex)\n        {\n            _logger.LogError(ex, \"\u6253\u5f00\u4e32\u53e3 {PortName} \u5931\u8d25\", PortName);\n            return false;\n        }\n    }\n\n    \/\/\/ &lt;summary&gt;\n    \/\/\/ \u626b\u63cf\u4e32\u53e3\u8bbe\u5907\n    \/\/\/ &lt;\/summary&gt;\n    public async Task&lt;List&lt;byte&gt;&gt; ScanDevicesAsync(\n        byte startAddress = 1,\n        byte endAddress = 247,\n        CancellationToken cancellationToken = default)\n    {\n        var foundDevices = new List&lt;byte&gt;();\n\n        _logger.LogInformation(\"\u5f00\u59cb\u626b\u63cf\u8bbe\u5907 (\u5730\u5740\u8303\u56f4: {StartAddress}-{EndAddress})\", startAddress, endAddress);\n\n        for (byte slaveAddress = startAddress; slaveAddress &lt;= endAddress; slaveAddress++)\n        {\n            if (cancellationToken.IsCancellationRequested)\n            {\n                break;\n            }\n\n            try\n            {\n                \/\/ \u5c1d\u8bd5\u8bfb\u53d6\u8bbe\u5907\u6807\u8bc6\n                var result = await Task.Run(() =&gt;\n                    _modbusMaster!.ReadDeviceIdentification(slaveAddress, 0x01),\n                    cancellationToken);\n\n                if (result != null)\n                {\n                    foundDevices.Add(slaveAddress);\n                    _logger.LogInformation(\"\n<\/code><\/pre>\n<span id=\"magicpostMarker\"><\/span>","protected":false},"excerpt":{"rendered":"<p>C#\/.NET\u5de5\u4e1a\u81ea\u52a8\u5316\u5f00\u53d1\u5b8c\u5168\u6307\u5357\uff1a\u6784\u5efa\u4f01\u4e1a\u7ea7\u5de5\u4e1a\u5e94\u7528 \u5f15&#8230;<\/p>","protected":false},"author":4698,"featured_media":27977,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":"","_members_access_role":[],"_members_access_error":""},"categories":[691],"tags":[2542,1846,2543,2544,1767],"class_list":["post-40580","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-modbus-code-base","tag-net","tag-c","tag-nmodbus","tag-2544","tag-1767"],"_links":{"self":[{"href":"https:\/\/www.modbus.cn\/en\/wp-json\/wp\/v2\/posts\/40580","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.modbus.cn\/en\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.modbus.cn\/en\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.modbus.cn\/en\/wp-json\/wp\/v2\/users\/4698"}],"replies":[{"embeddable":true,"href":"https:\/\/www.modbus.cn\/en\/wp-json\/wp\/v2\/comments?post=40580"}],"version-history":[{"count":0,"href":"https:\/\/www.modbus.cn\/en\/wp-json\/wp\/v2\/posts\/40580\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.modbus.cn\/en\/wp-json\/wp\/v2\/media\/27977"}],"wp:attachment":[{"href":"https:\/\/www.modbus.cn\/en\/wp-json\/wp\/v2\/media?parent=40580"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.modbus.cn\/en\/wp-json\/wp\/v2\/categories?post=40580"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.modbus.cn\/en\/wp-json\/wp\/v2\/tags?post=40580"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}