C# Modbus RTU 多从站控制全攻略:一端口,双轴控制

C# Modbus RTU 多从站控制全攻略:一端口,双轴控制

前言:当 Modbus 遇上多从站

在工业自动化领域,Modbus RTU 是一种常见的通信协议,但当多个设备共享同一个 COM 口时,就像让多个哑巴通过同一个话筒说话。本文将详细介绍如何使用 C# 实现一个 COM 口控制多个 AZ 驱动器。

一、项目架构设计

1.1 技术栈选择

csharp 复制代码
// 项目使用的技术栈
- .NET 6.0/8.0          // 跨平台,高性能
- NModbus               // Modbus 通信库(关键)
- WPF + MVVM            // 桌面应用架构
- CommunityToolkit.Mvvm // MVVM 开发工具
- Dependency Injection  // 依赖注入管理
- ILogger               // 日志记录

1.2 项目结构

复制代码
ModbusMultiSlave/
├── Controllers/           // Modbus 通信控制器
│   ├── AzDriverController.cs
│   └── AzDriverAddresses.cs
├── Services/             // 业务服务层
│   ├── IAxisService.cs
│   ├── AxisService.cs
│   └── PlcAddressService.cs
├── Models/               // 数据模型
│   ├── AzDriverStatus.cs
│   └── Alarm.cs
├── ViewModels/           // 视图模型
│   ├── TestViewModel.cs
│   └── MainViewModel.cs
├── Views/                // 视图
│   └── TestPage.xaml
├── Interfaces/           // 接口定义
│   └── IAxisService.cs
└── App.xaml.cs          // 应用入口

二、核心代码实现

2.1 Modbus RTU 控制器类

AzDriverController.cs

csharp 复制代码
using System;
using System.Collections.Concurrent;
using System.IO.Ports;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using NModbus;
using NModbus.Serial;

namespace ModbusMultiSlave.Controllers
{
    /// <summary>
    /// AZ 驱动器控制器 - 支持多个从站共享串口
    /// </summary>
    public class AzDriverController : IDisposable
    {
        #region 私有字段
        private SerialPort _serialPort;
        private IModbusSerialMaster _master;
        private readonly object _lock = new object();
        private bool _isConnected = false;
        private string _currentPortName;
        private int _currentBaudRate;
        private bool _isDisposed = false;
        private readonly ILogger<AzDriverController> _logger;
        
        // 记录每个从站是否在线
        private readonly ConcurrentDictionary<byte, bool> _slaveOnlineStatus = new ConcurrentDictionary<byte, bool>();
        
        // 通信统计和控制
        private int _communicationCount = 0;
        private byte _lastSlaveId = 0;
        private DateTime _lastCommunicationTime = DateTime.MinValue;
        private const int MIN_COMM_INTERVAL_MS = 50; // 最小通信间隔
        #endregion

        #region 属性
        public bool IsConnected => _isConnected && _serialPort?.IsOpen == true;
        public string PortName => _currentPortName;
        public int BaudRate => _currentBaudRate;
        public int CommunicationCount => _communicationCount;
        #endregion

        #region 构造函数
        public AzDriverController(ILogger<AzDriverController> logger)
        {
            _logger = logger ?? throw new ArgumentNullException(nameof(logger));
            _logger.LogInformation("AzDriverController 实例已创建");
        }
        #endregion

        #region 连接管理
        /// <summary>
        /// 连接到串口
        /// </summary>
        public bool Connect(string portName = "COM11", int baudRate = 115200)
        {
            lock (_lock)
            {
                try
                {
                    _logger.LogInformation("尝试连接串口: {PortName}@{BaudRate}bps", portName, baudRate);
                    
                    // 如果已经连接并且参数相同,直接返回成功
                    if (_isConnected && _currentPortName == portName && _currentBaudRate == baudRate)
                    {
                        _logger.LogInformation("串口已连接相同参数,直接返回成功");
                        return true;
                    }

                    // 如果已有连接但参数不同,先断开
                    if (_isConnected)
                    {
                        _logger.LogInformation("已有连接但参数不同,先断开连接");
                        DisconnectInternal();
                    }

                    _currentPortName = portName;
                    _currentBaudRate = baudRate;

                    // 创建并配置串口
                    _serialPort = new SerialPort(portName, baudRate, Parity.Even, 8, StopBits.One)
                    {
                        Handshake = Handshake.None,
                        ReadTimeout = 2000,      // 适当增加超时时间
                        WriteTimeout = 2000,
                        RtsEnable = true,        // RS485 需要 RTS 控制
                        DtrEnable = true
                    };

                    // 打开串口
                    _serialPort.Open();
                    _logger.LogInformation("串口打开成功");

                    // 创建 Modbus RTU 主站
                    var factory = new ModbusFactory();
                    _master = factory.CreateRtuMaster(_serialPort);
                    
                    // 配置 Modbus 参数
                    _master.Transport.Retries = 2;
                    _master.Transport.ReadTimeout = 2000;
                    _master.Transport.WriteTimeout = 2000;
                    _master.Transport.SlaveBusyUsesRetryCount = false;
                    
                    _isConnected = true;
                    _slaveOnlineStatus.Clear();
                    
                    _logger.LogInformation("✅ AZ 驱动器连接成功: {PortName}@{BaudRate}bps", portName, baudRate);
                    return true;
                }
                catch (Exception ex)
                {
                    _logger.LogError(ex, "❌ AZ 驱动器连接失败: {PortName}@{BaudRate}bps", portName, baudRate);
                    DisconnectInternal();
                    return false;
                }
            }
        }

        /// <summary>
        /// 断开连接
        /// </summary>
        public void Disconnect()
        {
            lock (_lock)
            {
                DisconnectInternal();
            }
        }

        private void DisconnectInternal()
        {
            if (!_isConnected) return;
            
            _logger.LogInformation("正在断开串口连接");
            _isConnected = false;
            _slaveOnlineStatus.Clear();

            try
            {
                _master?.Dispose();
                _master = null;
                _logger.LogInformation("Modbus 主站已释放");
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "释放 Modbus 主站时发生错误");
            }

            try
            {
                if (_serialPort?.IsOpen == true)
                {
                    _serialPort.Close();
                    _logger.LogInformation("串口已关闭");
                }
                _serialPort?.Dispose();
                _serialPort = null;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "关闭串口时发生错误");
            }

            _logger.LogInformation("AZ 驱动器已断开连接");
        }
        #endregion

        #region 通信辅助方法
        /// <summary>
        /// 确保通信间隔,避免冲突
        /// </summary>
        private void EnsureCommunicationInterval(byte slaveId)
        {
            if (_lastSlaveId == 0 || _lastSlaveId == slaveId)
            {
                // 相同从站,不需要额外间隔
                return;
            }
            
            // 不同从站之间需要间隔
            var elapsed = DateTime.Now - _lastCommunicationTime;
            if (elapsed.TotalMilliseconds < MIN_COMM_INTERVAL_MS)
            {
                var waitTime = (int)(MIN_COMM_INTERVAL_MS - elapsed.TotalMilliseconds);
                _logger.LogDebug("等待通信间隔: {WaitTime}ms (从站 {From} -> {To})", 
                    waitTime, _lastSlaveId, slaveId);
                Thread.Sleep(waitTime);
            }
            
            _lastSlaveId = slaveId;
        }

        /// <summary>
        /// 检查从站是否在线
        /// </summary>
        public bool CheckSlaveOnline(byte slaveId)
        {
            lock (_lock)
            {
                if (!_isConnected) 
                {
                    _logger.LogWarning("检查从站 {SlaveId} 在线状态失败: 未连接", slaveId);
                    return false;
                }

                try
                {
                    _logger.LogDebug("检查从站 {SlaveId} 是否在线", slaveId);
                    
                    // 确保通信间隔
                    EnsureCommunicationInterval(slaveId);
                    
                    // 尝试读取状态字来判断从站是否在线
                    var registers = _master.ReadHoldingRegisters(slaveId,
                        (ushort)(AzDriverAddresses.StatusWord - 1), 1);

                    bool online = registers != null && registers.Length > 0;
                    _slaveOnlineStatus[slaveId] = online;
                    
                    _logger.LogInformation("从站 {SlaveId} 在线状态: {Online}", 
                        slaveId, online ? "在线" : "离线");
                    
                    _lastCommunicationTime = DateTime.Now;
                    return online;
                }
                catch (Exception ex)
                {
                    _logger.LogWarning("检查从站 {SlaveId} 在线状态失败: {Message}", slaveId, ex.Message);
                    _slaveOnlineStatus[slaveId] = false;
                    return false;
                }
            }
        }

        /// <summary>
        /// 获取从站在线状态
        /// </summary>
        public bool IsSlaveOnline(byte slaveId)
        {
            return _slaveOnlineStatus.TryGetValue(slaveId, out bool online) && online;
        }
        #endregion

        #region 基本读写操作
        /// <summary>
        /// 读取单个寄存器
        /// </summary>
        public ushort ReadRegister(byte slaveId, ushort address)
        {
            lock (_lock)
            {
                try
                {
                    _communicationCount++;
                    _logger.LogDebug("#{Count} 读取寄存器 [Slave:{SlaveId}, Addr:{Address}(0x{Address:X4})]", 
                        _communicationCount, slaveId, address, address);
                    
                    if (!_isConnected)
                    {
                        _logger.LogWarning("读取寄存器失败:未连接");
                        return 0;
                    }

                    // 确保通信间隔
                    EnsureCommunicationInterval(slaveId);
                    
                    var registers = _master.ReadHoldingRegisters(slaveId, (ushort)(address - 1), 1);
                    var result = registers?.Length > 0 ? registers[0] : (ushort)0;
                    
                    _logger.LogDebug("寄存器读取结果 [Slave:{SlaveId}]: 0x{Result:X4} ({Result})", 
                        slaveId, result, result);
                    
                    _slaveOnlineStatus[slaveId] = true;
                    _lastCommunicationTime = DateTime.Now;
                    return result;
                }
                catch (Exception ex)
                {
                    _logger.LogError(ex, "读取寄存器失败 [Slave:{SlaveId}, Addr:{Address}]", slaveId, address);
                    _slaveOnlineStatus[slaveId] = false;
                    return 0;
                }
            }
        }

        /// <summary>
        /// 写入单个寄存器
        /// </summary>
        public bool WriteRegister(byte slaveId, ushort address, ushort value)
        {
            lock (_lock)
            {
                try
                {
                    _communicationCount++;
                    _logger.LogDebug("#{Count} 写入寄存器 [Slave:{SlaveId}, Addr:{Address}, Value:{Value}(0x{Value:X4})]", 
                        _communicationCount, slaveId, address, value, value);
                    
                    if (!_isConnected)
                    {
                        _logger.LogWarning("写入寄存器失败:未连接");
                        return false;
                    }

                    // 确保通信间隔
                    EnsureCommunicationInterval(slaveId);
                    
                    _master.WriteSingleRegister(slaveId, (ushort)(address - 1), value);
                    
                    _logger.LogDebug("寄存器写入成功 [Slave:{SlaveId}]", slaveId);
                    
                    _slaveOnlineStatus[slaveId] = true;
                    _lastCommunicationTime = DateTime.Now;
                    return true;
                }
                catch (Exception ex)
                {
                    _logger.LogError(ex, "写入寄存器失败 [Slave:{SlaveId}, Addr:{Address}]", slaveId, address);
                    _slaveOnlineStatus[slaveId] = false;
                    return false;
                }
            }
        }

        /// <summary>
        /// 读取 32 位数据(两个寄存器)
        /// </summary>
        public int Read32BitData(byte slaveId, ushort startAddress)
        {
            lock (_lock)
            {
                try
                {
                    _communicationCount++;
                    _logger.LogDebug("#{Count} 读取32位数据 [Slave:{SlaveId}, Addr:{Address}(0x{Address:X4})]", 
                        _communicationCount, slaveId, startAddress, startAddress);
                    
                    if (!_isConnected)
                    {
                        _logger.LogWarning("读取32位数据失败:未连接");
                        return 0;
                    }

                    // 确保通信间隔
                    EnsureCommunicationInterval(slaveId);
                    
                    var registers = _master.ReadHoldingRegisters(slaveId, (ushort)(startAddress - 1), 2);

                    if (registers.Length != 2)
                    {
                        _logger.LogWarning("读取32位数据失败:返回数据长度错误");
                        return 0;
                    }

                    int high = registers[0];  // 高位寄存器
                    int low = registers[1];   // 低位寄存器
                    
                    _logger.LogDebug("寄存器原始值:高位={High}(0x{High:X4}), 低位={Low}(0x{Low:X4})",
                        high, high, low, low);
                    
                    // 组合成 32 位整数(高位在前,标准 Modbus)
                    int value = (high << 16) | low;
                    
                    // 处理有符号整数(二进制补码)
                    if ((value & 0x80000000) != 0)  // 最高位为 1,表示负数
                    {
                        value = (int)(value - 0x100000000);
                        _logger.LogDebug("有符号负数转换:原始值=0x{Value:X8}, 转换后={Result}",
                            (uint)value + 0x100000000, value);
                    }
                    else
                    {
                        _logger.LogDebug("有符号正数:值={Value}", value);
                    }

                    _slaveOnlineStatus[slaveId] = true;
                    _lastCommunicationTime = DateTime.Now;
                    return value;
                }
                catch (Exception ex)
                {
                    _logger.LogError(ex, "读取32位数据失败 [Slave:{SlaveId}, Addr:{Address}]", 
                        slaveId, startAddress);
                    _slaveOnlineStatus[slaveId] = false;
                    return 0;
                }
            }
        }

        /// <summary>
        /// 写入 32 位数据(两个寄存器)
        /// </summary>
        public bool Write32BitData(byte slaveId, ushort startAddress, int value)
        {
            lock (_lock)
            {
                try
                {
                    _communicationCount++;
                    _logger.LogDebug("#{Count} 写入32位数据 [Slave:{SlaveId}, Addr:{Address}, 值:{Value}]", 
                        _communicationCount, slaveId, startAddress, value);
                    
                    if (!_isConnected)
                    {
                        _logger.LogWarning("写入32位数据失败:未连接");
                        return false;
                    }

                    // 确保通信间隔
                    EnsureCommunicationInterval(slaveId);
                    
                    // 处理有符号整数(二进制补码)
                    uint unsignedValue;
                    if (value < 0)
                    {
                        // 负数:转换为补码
                        unsignedValue = (uint)(0x100000000 + value);
                        _logger.LogDebug("负数{Value}的补码:0x{UnsignedValue:X8}", 
                            value, unsignedValue);
                    }
                    else
                    {
                        // 正数:直接转换
                        unsignedValue = (uint)value;
                    }
                    
                    // 高位在前(标准 Modbus)
                    ushort high = (ushort)((unsignedValue >> 16) & 0xFFFF);
                    ushort low = (ushort)(unsignedValue & 0xFFFF);
                    
                    _logger.LogDebug("分解为寄存器:高位={High}(0x{High:X4}), 低位={Low}(0x{Low:X4})",
                        high, high, low, low);
                    
                    // 写入寄存器
                    var values = new ushort[] { high, low };
                    _master.WriteMultipleRegisters(slaveId, (ushort)(startAddress - 1), values);
                    
                    _logger.LogDebug("✅ 32位数据写入成功");
                    
                    _slaveOnlineStatus[slaveId] = true;
                    _lastCommunicationTime = DateTime.Now;
                    return true;
                }
                catch (Exception ex)
                {
                    _logger.LogError(ex, "写入32位数据失败");
                    _slaveOnlineStatus[slaveId] = false;
                    return false;
                }
            }
        }
        #endregion

        #region 核心控制功能
        /// <summary>
        /// 发送控制命令(关键:发送后自动清零)
        /// </summary>
        public bool SendControlCommand(byte slaveId, ushort command)
        {
            lock (_lock)
            {
                try
                {
                    _communicationCount++;
                    _logger.LogDebug("#{Count} 发送控制命令 [Slave:{SlaveId}, Cmd:0x{Command:X4}]", 
                        _communicationCount, slaveId, command);
                    
                    if (!_isConnected)
                    {
                        _logger.LogWarning("发送控制命令失败:未连接");
                        return false;
                    }

                    // 确保通信间隔
                    EnsureCommunicationInterval(slaveId);
                    
                    // 1. 写入控制命令
                    _master.WriteSingleRegister(slaveId, 
                        (ushort)(AzDriverAddresses.ControlWord - 1), command);
                    
                    // 2. 短暂延迟确保命令被接收
                    Thread.Sleep(50);
                    
                    // 3. 重要:立即将控制字清零
                    _master.WriteSingleRegister(slaveId, 
                        (ushort)(AzDriverAddresses.ControlWord - 1), 0);
                    
                    _logger.LogDebug("控制命令发送完成 [Slave:{SlaveId}]", slaveId);
                    
                    _slaveOnlineStatus[slaveId] = true;
                    _lastCommunicationTime = DateTime.Now;
                    return true;
                }
                catch (Exception ex)
                {
                    _logger.LogError(ex, "发送控制命令失败 [Slave:{SlaveId}]", slaveId);
                    _slaveOnlineStatus[slaveId] = false;
                    return false;
                }
            }
        }

        /// <summary>
        /// 设置绝对定位模式
        /// </summary>
        public bool SetAbsoluteMode(byte slaveId)
        {
            _logger.LogInformation("设置从站 {SlaveId} 为绝对定位模式", slaveId);
            return WriteRegister(slaveId, AzDriverAddresses.OperationMode, 1);
        }

        /// <summary>
        /// 设置原点位置
        /// </summary>
        public bool SetOrigin(byte slaveId)
        {
            _logger.LogInformation("从站 {SlaveId} 设置原点", slaveId);
            
            // 写入位置预置命令 ON
            if (!WriteRegister(slaveId, AzDriverAddresses.PositionPreset, 1))
                return false;

            // 短暂延迟
            Thread.Sleep(100);

            // 写入位置预置命令 OFF
            return WriteRegister(slaveId, AzDriverAddresses.PositionPreset, 0);
        }

        /// <summary>
        /// 执行绝对定位
        /// </summary>
        public bool MoveAbsolute(byte slaveId, int position, int speed)
        {
            lock (_lock)
            {
                _logger.LogInformation("从站 {SlaveId} 开始绝对定位: 位置={Position}, 速度={Speed}", 
                    slaveId, position, speed);
                
                if (!_isConnected)
                {
                    _logger.LogWarning("从站 {SlaveId} 绝对定位失败: 未连接", slaveId);
                    return false;
                }

                try
                {
                    // 1. 确保为绝对定位模式
                    if (!SetAbsoluteMode(slaveId))
                    {
                        _logger.LogWarning("从站 {SlaveId} 设置绝对定位模式失败", slaveId);
                        return false;
                    }

                    // 2. 设置目标位置
                    if (!Write32BitData(slaveId, AzDriverAddresses.TargetPosition, position))
                    {
                        _logger.LogWarning("从站 {SlaveId} 设置目标位置失败", slaveId);
                        return false;
                    }

                    // 3. 设置运行速度
                    if (!Write32BitData(slaveId, AzDriverAddresses.RunSpeed, speed))
                    {
                        _logger.LogWarning("从站 {SlaveId} 设置运行速度失败", slaveId);
                        return false;
                    }

                    // 4. 发送启动命令
                    var result = SendControlCommand(slaveId, AzDriverAddresses.ControlCommands.START);
                    
                    if (result)
                        _logger.LogInformation("从站 {SlaveId} 绝对定位命令已发送", slaveId);
                    else
                        _logger.LogWarning("从站 {SlaveId} 绝对定位命令发送失败", slaveId);
                        
                    return result;
                }
                catch (Exception ex)
                {
                    _logger.LogError(ex, "从站 {SlaveId} 绝对定位失败", slaveId);
                    _slaveOnlineStatus[slaveId] = false;
                    return false;
                }
            }
        }

        /// <summary>
        /// 执行原点复归
        /// </summary>
        public bool GoHome(byte slaveId)
        {
            _logger.LogInformation("从站 {SlaveId} 执行原点复归", slaveId);
            return SendControlCommand(slaveId, AzDriverAddresses.ControlCommands.ZHOME);
        }

        /// <summary>
        /// 停止运动
        /// </summary>
        public bool Stop(byte slaveId)
        {
            _logger.LogInformation("从站 {SlaveId} 停止运动", slaveId);
            return SendControlCommand(slaveId, AzDriverAddresses.ControlCommands.STOP);
        }

        /// <summary>
        /// 复位报警
        /// </summary>
        public bool ResetAlarm(byte slaveId)
        {
            _logger.LogInformation("从站 {SlaveId} 复位报警", slaveId);
            return SendControlCommand(slaveId, AzDriverAddresses.ControlCommands.ALM_RST);
        }

        /// <summary>
        /// 急停
        /// </summary>
        public bool EmergencyStop(byte slaveId)
        {
            _logger.LogInformation("从站 {SlaveId} 急停", slaveId);
            return SendControlCommand(slaveId, AzDriverAddresses.ControlCommands.ALL_OFF);
        }

        /// <summary>
        /// 设置运行速度
        /// </summary>
        public bool SetSpeed(byte slaveId, int speed)
        {
            _logger.LogInformation("从站 {SlaveId} 设置运行速度: {Speed}", slaveId, speed);
            return Write32BitData(slaveId, AzDriverAddresses.RunSpeed, speed);
        }

        /// <summary>
        /// 读取驱动器状态
        /// </summary>
        public AzDriverStatus ReadStatus(byte slaveId)
        {
            var status = new AzDriverStatus();

            lock (_lock)
            {
                _logger.LogDebug("读取从站 {SlaveId} 状态", slaveId);
                
                if (!_isConnected)
                {
                    _logger.LogWarning("读取从站 {SlaveId} 状态失败: 未连接", slaveId);
                    return status;
                }

                try
                {
                    // 读取状态字
                    ushort statusWord = ReadRegister(slaveId, AzDriverAddresses.StatusWord);
                    status.IsReady = (statusWord & AzDriverAddresses.StatusBits.READY) != 0;
                    status.IsMoving = (statusWord & AzDriverAddresses.StatusBits.MOVE) != 0;
                    status.InPosition = (statusWord & AzDriverAddresses.StatusBits.IN_POS) != 0;

                    _logger.LogDebug("从站 {SlaveId} 状态字: 0x{StatusWord:X4}, 就绪={IsReady}, 移动={IsMoving}, 到位={InPosition}", 
                        slaveId, statusWord, status.IsReady, status.IsMoving, status.InPosition);

                    // 读取报警代码
                    status.AlarmCode = ReadRegister(slaveId, AzDriverAddresses.AlarmCode);
                    _logger.LogDebug("从站 {SlaveId} 报警代码: 0x{AlarmCode:X4}", slaveId, status.AlarmCode);

                    // 读取实际位置
                    status.ActualPosition = Read32BitData(slaveId, AzDriverAddresses.ActualPosition);
                    _logger.LogDebug("从站 {SlaveId} 实际位置: {ActualPosition}", slaveId, status.ActualPosition);

                    // 读取实际速度
                    status.ActualSpeed = Read32BitData(slaveId, AzDriverAddresses.ActualSpeed);
                    _logger.LogDebug("从站 {SlaveId} 实际速度: {ActualSpeed}", slaveId, status.ActualSpeed);

                    // 读取指令位置
                    status.CommandPosition = Read32BitData(slaveId, AzDriverAddresses.CommandPosition);
                    _logger.LogDebug("从站 {SlaveId} 指令位置: {CommandPosition}", slaveId, status.CommandPosition);

                    _slaveOnlineStatus[slaveId] = true;
                    _logger.LogDebug("从站 {SlaveId} 状态读取完成", slaveId);
                    return status;
                }
                catch (Exception ex)
                {
                    _logger.LogError(ex, "读取从站 {SlaveId} 状态失败", slaveId);
                    _slaveOnlineStatus[slaveId] = false;
                    return status;
                }
            }
        }
        #endregion

        #region 诊断工具
        /// <summary>
        /// 诊断 32 位数据读取问题
        /// </summary>
        public void Diagnose32BitData(byte slaveId, ushort startAddress, int expectedValue = 0)
        {
            lock (_lock)
            {
                try
                {
                    _logger.LogInformation("=== 32位数据诊断 [Slave:{SlaveId}, Addr:{Address}] ===", 
                        slaveId, startAddress);
                    
                    if (!_isConnected)
                    {
                        _logger.LogWarning("未连接,无法诊断");
                        return;
                    }

                    // 读取两个寄存器的原始值
                    var registers = _master.ReadHoldingRegisters(slaveId, (ushort)(startAddress - 1), 2);
                    
                    if (registers.Length == 2)
                    {
                        ushort reg1 = registers[0];  // 第一个寄存器
                        ushort reg2 = registers[1];  // 第二个寄存器
                        
                        _logger.LogInformation("寄存器原始值:");
                        _logger.LogInformation("  寄存器1({Address}) = {Reg1} (0x{Reg1:X4})", 
                            startAddress, reg1, reg1);
                        _logger.LogInformation("  寄存器2({Address}) = {Reg2} (0x{Reg2:X4})", 
                            (ushort)(startAddress + 1), reg2, reg2);
                        
                        // 不同解析方式
                        _logger.LogInformation("不同解析方式:");
                        
                        // 1. 高位在前有符号(标准 Modbus)
                        int valueHighFirstSigned = (reg1 << 16) | reg2;
                        if ((valueHighFirstSigned & 0x80000000) != 0)
                            valueHighFirstSigned = (int)(valueHighFirstSigned - 0x100000000);
                        _logger.LogInformation("  1. 高位在前有符号:{Value}", valueHighFirstSigned);
                        
                        // 2. 低位在前有符号
                        int valueLowFirstSigned = (reg2 << 16) | reg1;
                        if ((valueLowFirstSigned & 0x80000000) != 0)
                            valueLowFirstSigned = (int)(valueLowFirstSigned - 0x100000000);
                        _logger.LogInformation("  2. 低位在前有符号:{Value}", valueLowFirstSigned);
                        
                        // 3. 高位在前无符号
                        uint valueHighFirstUnsigned = (uint)((reg1 << 16) | reg2);
                        _logger.LogInformation("  3. 高位在前无符号:{Value}", valueHighFirstUnsigned);
                        
                        // 4. 低位在前无符号
                        uint valueLowFirstUnsigned = (uint)((reg2 << 16) | reg1);
                        _logger.LogInformation("  4. 低位在前无符号:{Value}", valueLowFirstUnsigned);
                        
                        // 如果有期望值,显示期望的寄存器值
                        if (expectedValue != 0)
                        {
                            _logger.LogInformation("期望值 {Expected} 对应的寄存器值:", expectedValue);
                            
                            if (expectedValue >= 0)
                            {
                                // 正数
                                ushort expectedHigh = (ushort)((expectedValue >> 16) & 0xFFFF);
                                ushort expectedLow = (ushort)(expectedValue & 0xFFFF);
                                _logger.LogInformation("  高位在前:高位=0x{High:X4}({High}), 低位=0x{Low:X4}({Low})",
                                    expectedHigh, expectedHigh, expectedLow, expectedLow);
                            }
                            else
                            {
                                // 负数(使用二进制补码)
                                uint negativeValue = (uint)(0x100000000 + expectedValue);
                                ushort expectedHigh = (ushort)((negativeValue >> 16) & 0xFFFF);
                                ushort expectedLow = (ushort)(negativeValue & 0xFFFF);
                                
                                _logger.LogInformation("  负数{Expected}的补码:0x{NegValue:X8}", 
                                    expectedValue, negativeValue);
                                _logger.LogInformation("  高位在前:高位=0x{High:X4}({High}), 低位=0x{Low:X4}({Low})",
                                    expectedHigh, expectedHigh, expectedLow, expectedLow);
                            }
                        }
                        
                        // 特殊值分析
                        if (reg1 == 0xFFFF && reg2 == 0xC180)
                        {
                            _logger.LogInformation("⚠️ 检测到特殊值组合:0xFFFF, 0xC180");
                            _logger.LogInformation("  这个组合表示有符号32位整数:-16000");
                        }
                    }
                }
                catch (Exception ex)
                {
                    _logger.LogError(ex, "诊断32位数据失败");
                }
                
                _logger.LogInformation("=== 诊断结束 ===");
            }
        }
        #endregion

        #region 资源清理
        public void Dispose()
        {
            if (_isDisposed) return;

            _logger.LogInformation("开始释放 AzDriverController 资源");
            DisconnectInternal();
            _isDisposed = true;

            _logger.LogInformation("AzDriverController 资源已释放");
            GC.SuppressFinalize(this);
        }

        ~AzDriverController()
        {
            Dispose();
        }
        #endregion
    }
}

2.2 驱动器地址定义

AzDriverAddresses.cs

csharp 复制代码
using System;

namespace ModbusMultiSlave.Controllers
{
    /// <summary>
    /// AZ 驱动器寄存器地址定义
    /// </summary>
    public static class AzDriverAddresses
    {
        // 寄存器地址(基于 1 的地址)
        public const ushort StatusWord = 0x0001;        // 状态字
        public const ushort AlarmCode = 0x0002;         // 报警代码
        public const ushort ActualPosition = 0x0003;    // 实际位置(32位,起始地址)
        public const ushort ActualSpeed = 0x0005;       // 实际速度(32位)
        public const ushort CommandPosition = 0x0007;   // 指令位置(32位)
        public const ushort TargetPosition = 0x0009;    // 目标位置(32位)
        public const ushort RunSpeed = 0x000B;          // 运行速度(32位)
        public const ushort ControlWord = 0x000D;       // 控制字
        public const ushort OperationMode = 0x000E;     // 操作模式
        public const ushort PositionPreset = 0x000F;    // 位置预置

        // 状态字位定义
        public static class StatusBits
        {
            public const ushort READY = 0x0001;      // 位0:就绪状态
            public const ushort MOVE = 0x0002;       // 位1:移动状态
            public const ushort IN_POS = 0x0004;     // 位2:到位标志
            public const ushort ALARM = 0x0008;      // 位3:报警状态
            public const ushort ENABLED = 0x0010;    // 位4:使能状态
            public const ushort HOME_COMPLETE = 0x0020; // 位5:回零完成
        }

        // 控制命令
        public static class ControlCommands
        {
            public const ushort START = 0x0001;      // 启动
            public const ushort STOP = 0x0002;       // 停止
            public const ushort ZHOME = 0x0004;      // 原点复归
            public const ushort ALM_RST = 0x0008;    // 报警复位
            public const ushort ALL_OFF = 0x0010;    // 全部关闭(急停)
        }

        // 操作模式
        public static class OperationModes
        {
            public const ushort ABSOLUTE = 0x0001;   // 绝对定位模式
            public const ushort RELATIVE = 0x0002;   // 相对定位模式
            public const ushort JOG = 0x0003;        // 点动模式
        }
    }
}

2.3 驱动器状态模型

AzDriverStatus.cs

csharp 复制代码
using System;

namespace ModbusMultiSlave.Models
{
    /// <summary>
    /// AZ 驱动器状态信息
    /// </summary>
    public class AzDriverStatus
    {
        // 驱动器状态
        public bool IsReady { get; set; } = false;
        public bool IsMoving { get; set; } = false;
        public bool InPosition { get; set; } = false;
        public bool IsEnabled { get; set; } = false;
        public bool HomeComplete { get; set; } = false;
        
        // 报警和故障信息
        public ushort AlarmCode { get; set; } = 0;
        public bool HasAlarm => AlarmCode != 0;
        
        // 位置信息
        public int ActualPosition { get; set; } = 0;     // 实际位置
        public int CommandPosition { get; set; } = 0;    // 指令位置
        public int TargetPosition { get; set; } = 0;     // 目标位置
        
        // 速度信息
        public int ActualSpeed { get; set; } = 0;        // 实际速度
        public int RunSpeed { get; set; } = 0;           // 运行速度
        
        // 状态显示
        public string StatusText
        {
            get
            {
                if (AlarmCode != 0) return $"报警: {GetAlarmDescription()}";
                if (IsMoving) return "运行中";
                if (IsReady) return "就绪";
                if (HomeComplete) return "回零完成";
                return "未知";
            }
        }
        
        /// <summary>
        /// 获取报警描述
        /// </summary>
        public string GetAlarmDescription()
        {
            return AlarmCode switch
            {
                0 => "正常",
                1 => "过电流",
                2 => "过电压",
                3 => "欠电压",
                4 => "电机过热",
                5 => "驱动器过热",
                6 => "过载",
                7 => "编码器异常",
                8 => "位置误差过大",
                9 => "通信异常",
                10 => "参数错误",
                11 => "硬件故障",
                12 => "软件故障",
                13 => "电源故障",
                14 => "紧急停止",
                15 => "限位开关触发",
                _ => $"未知报警 (0x{AlarmCode:X4})"
            };
        }
        
        /// <summary>
        /// 转换为字符串表示
        /// </summary>
        public override string ToString()
        {
            return $"状态: {StatusText}, " +
                   $"位置: {ActualPosition}, " +
                   $"速度: {ActualSpeed}, " +
                   $"报警: {GetAlarmDescription()}";
        }
    }
}

2.4 轴服务接口

IAxisService.cs

csharp 复制代码
using System;
using System.Threading.Tasks;

namespace ModbusMultiSlave.Services
{
    /// <summary>
    /// 轴服务接口
    /// </summary>
    public interface IAxisService : IDisposable
    {
        // 轴基本信息
        string AxisName { get; }
        byte SlaveId { get; }
        
        // 目标参数
        int TargetPosition { get; set; }
        int TargetSpeed { get; set; }
        
        // 状态信息
        int ActualPosition { get; }
        int CommandPosition { get; }
        int ActualSpeed { get; }
        string AlarmDescription { get; }
        bool IsReady { get; }
        bool IsMoving { get; }
        bool IsConnected { get; }
        string Status { get; }
        ushort AlarmCode { get; }
        
        // 连接管理
        Task<bool> ConnectAsync(string portName = "COM11", int baudRate = 115200);
        Task DisconnectAsync();
        
        // 控制命令
        Task<bool> SetOriginAsync();
        Task<bool> GoHomeAsync();
        Task<bool> MoveAbsoluteAsync();
        Task<bool> StopAsync();
        Task<bool> EmergencyStopAsync();
        Task<bool> ResetAlarmAsync();
        Task<bool> SetSpeedAsync(int speed);
        
        // 状态监控
        Task UpdateStatusAsync();
        void StartMonitoring();
        void StopMonitoring();
        
        // 获取底层驱动控制器
        AzDriverController GetDriver();
    }
}

2.5 轴服务实现

AxisService.cs

csharp 复制代码
using System;
using System.Threading;
using System.Threading.Tasks;
using ModbusMultiSlave.Controllers;
using Microsoft.Extensions.Logging;

namespace ModbusMultiSlave.Services
{
    /// <summary>
    /// 轴服务实现
    /// </summary>
    public class AxisService : IAxisService
    {
        #region 私有字段
        private readonly AzDriverController _driver;
        private readonly byte _slaveId;
        private readonly ILogger<AxisService> _logger;
        private CancellationTokenSource _monitoringCts;
        private bool _isMonitoring = false;
        private bool _isDisposed = false;
        private bool _hasSetAbsoluteMode = false;
        #endregion

        #region 属性
        public string AxisName { get; }
        public byte SlaveId => _slaveId;
        public int TargetPosition { get; set; } = 0;
        public int TargetSpeed { get; set; } = 1000;
        public int ActualPosition { get; private set; }
        public int CommandPosition { get; private set; }
        public int ActualSpeed { get; private set; }
        public string AlarmDescription { get; private set; } = "正常";
        public bool IsReady { get; private set; }
        public bool IsMoving { get; private set; }
        public bool IsConnected => _driver?.IsConnected == true && _driver.IsSlaveOnline(_slaveId);
        public string Status { get; private set; } = "未连接";
        public ushort AlarmCode { get; private set; }
        #endregion

        #region 构造函数
        public AxisService(byte slaveId, string axisName, ILogger<AxisService> logger, AzDriverController driver)
        {
            _slaveId = slaveId;
            AxisName = axisName;
            _logger = logger ?? throw new ArgumentNullException(nameof(logger));
            _driver = driver ?? throw new ArgumentNullException(nameof(driver));
            
            _logger.LogInformation("创建轴服务: {AxisName} (从站号: {SlaveId})", axisName, slaveId);
        }
        #endregion

        #region 连接管理
        public async Task<bool> ConnectAsync(string portName = "COM11", int baudRate = 115200)
        {
            try
            {
                Status = "连接中...";
                _logger.LogInformation("{AxisName} 开始连接: {PortName}@{BaudRate}bps", 
                    AxisName, portName, baudRate);

                // 验证连接参数
                if (!ValidateConnectionParameters(portName, baudRate))
                {
                    Status = "参数错误";
                    return false;
                }

                // 连接到驱动器
                var result = await Task.Run(() => _driver.Connect(portName, baudRate));

                if (result)
                {
                    // 检查从站是否在线
                    _logger.LogInformation("{AxisName} 正在检查从站 {SlaveId} 是否在线", AxisName, _slaveId);
                    var isOnline = await Task.Run(() => _driver.CheckSlaveOnline(_slaveId));

                    if (isOnline)
                    {
                        // 设置绝对定位模式(只在首次连接时设置)
                        if (!_hasSetAbsoluteMode)
                        {
                            _logger.LogInformation("{AxisName} 设置绝对定位模式", AxisName);
                            await Task.Run(() => _driver.SetAbsoluteMode(_slaveId));
                            _hasSetAbsoluteMode = true;
                        }

                        Status = "已连接";
                        _logger.LogInformation("✅ {AxisName} 连接成功: 从站 {SlaveId} 在线", AxisName, _slaveId);
                    }
                    else
                    {
                        Status = "从站无响应";
                        _logger.LogWarning("❌ {AxisName} 连接失败: 从站 {SlaveId} 无响应", AxisName, _slaveId);
                        return false;
                    }
                }
                else
                {
                    Status = "连接失败";
                    _logger.LogWarning("❌ {AxisName} 连接失败", AxisName);
                }

                return result;
            }
            catch (Exception ex)
            {
                Status = "连接异常";
                _logger.LogError(ex, "⚠️ {AxisName} 连接异常", AxisName);
                return false;
            }
        }

        private bool ValidateConnectionParameters(string portName, int baudRate)
        {
            // 检查端口是否存在
            var ports = System.IO.Ports.SerialPort.GetPortNames();
            if (!ports.Contains(portName))
            {
                _logger.LogError("串口 {PortName} 不存在,可用端口: {AvailablePorts}", 
                    portName, string.Join(", ", ports));
                return false;
            }

            // 检查波特率是否有效
            var validBaudRates = new[] { 9600, 19200, 38400, 57600, 115200, 230400, 460800, 921600 };
            if (!validBaudRates.Contains(baudRate))
            {
                _logger.LogError("无效的波特率 {BaudRate},有效值: {ValidBaudRates}",
                    baudRate, string.Join(", ", validBaudRates));
                return false;
            }

            _logger.LogInformation("连接参数验证通过: {PortName}@{BaudRate}bps", portName, baudRate);
            return true;
        }

        public async Task DisconnectAsync()
        {
            try
            {
                StopMonitoring();
                _hasSetAbsoluteMode = false;
                Status = "未连接";
                _logger.LogInformation("{AxisName} 已断开连接", AxisName);
                await Task.CompletedTask;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "{AxisName} 断开连接异常", AxisName);
            }
        }
        #endregion

        #region 核心控制命令
        public async Task<bool> SetOriginAsync()
        {
            try
            {
                _logger.LogInformation("{AxisName} 设置原点", AxisName);
                var result = await Task.Run(() => _driver.SetOrigin(_slaveId));
                
                if (result)
                    _logger.LogInformation("✅ {AxisName} 设置原点成功", AxisName);
                else
                    _logger.LogWarning("❌ {AxisName} 设置原点失败", AxisName);
                    
                return result;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "⚠️ {AxisName} 设置原点失败", AxisName);
                return false;
            }
        }

        public async Task<bool> GoHomeAsync()
        {
            try
            {
                _logger.LogInformation("{AxisName} 回原点", AxisName);
                var result = await Task.Run(() => _driver.GoHome(_slaveId));
                
                if (result)
                    _logger.LogInformation("✅ {AxisName} 回原点命令已发送", AxisName);
                else
                    _logger.LogWarning("❌ {AxisName} 回原点命令发送失败", AxisName);
                    
                return result;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "⚠️ {AxisName} 回原点失败", AxisName);
                return false;
            }
        }

        public async Task<bool> MoveAbsoluteAsync()
        {
            try
            {
                _logger.LogInformation("{AxisName} 开始绝对定位: 位置={TargetPosition}, 速度={TargetSpeed}",
                    AxisName, TargetPosition, TargetSpeed);

                // 确保已设置绝对定位模式
                if (!_hasSetAbsoluteMode)
                {
                    _logger.LogInformation("{AxisName} 自动设置绝对定位模式", AxisName);
                    await Task.Run(() => _driver.SetAbsoluteMode(_slaveId));
                    _hasSetAbsoluteMode = true;
                }

                var result = await Task.Run(() => _driver.MoveAbsolute(_slaveId, TargetPosition, TargetSpeed));
                
                if (result)
                    _logger.LogInformation("✅ {AxisName} 绝对定位命令已发送", AxisName);
                else
                    _logger.LogWarning("❌ {AxisName} 绝对定位命令发送失败", AxisName);
                    
                return result;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "⚠️ {AxisName} 绝对定位失败", AxisName);
                return false;
            }
        }

        public async Task<bool> StopAsync()
        {
            try
            {
                _logger.LogInformation("{AxisName} 停止", AxisName);
                var result = await Task.Run(() => _driver.Stop(_slaveId));
                
                if (result)
                    _logger.LogInformation("✅ {AxisName} 停止命令已发送", AxisName);
                else
                    _logger.LogWarning("❌ {AxisName} 停止命令发送失败", AxisName);
                    
                return result;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "⚠️ {AxisName} 停止失败", AxisName);
                return false;
            }
        }

        public async Task<bool> EmergencyStopAsync()
        {
            try
            {
                _logger.LogInformation("{AxisName} 急停", AxisName);
                var result = await Task.Run(() => _driver.EmergencyStop(_slaveId));
                
                if (result)
                    _logger.LogInformation("✅ {AxisName} 急停命令已发送", AxisName);
                else
                    _logger.LogWarning("❌ {AxisName} 急停命令发送失败", AxisName);
                    
                return result;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "⚠️ {AxisName} 急停失败", AxisName);
                return false;
            }
        }

        public async Task<bool> ResetAlarmAsync()
        {
            try
            {
                _logger.LogInformation("{AxisName} 清理报警", AxisName);
                var result = await Task.Run(() => _driver.ResetAlarm(_slaveId));
                
                if (result)
                    _logger.LogInformation("✅ {AxisName} 清理报警命令已发送", AxisName);
                else
                    _logger.LogWarning("❌ {AxisName} 清理报警命令发送失败", AxisName);
                    
                return result;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "⚠️ {AxisName} 清理报警失败", AxisName);
                return false;
            }
        }

        public async Task<bool> SetSpeedAsync(int speed)
        {
            try
            {
                TargetSpeed = speed;
                _logger.LogInformation("{AxisName} 设置速度: {Speed}", AxisName, speed);
                var result = await Task.Run(() => _driver.SetSpeed(_slaveId, speed));
                
                if (result)
                    _logger.LogInformation("✅ {AxisName} 设置速度成功", AxisName);
                else
                    _logger.LogWarning("❌ {AxisName} 设置速度失败", AxisName);
                    
                return result;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "⚠️ {AxisName} 设置速度失败", AxisName);
                return false;
            }
        }
        #endregion

        #region 状态监控
        public async Task UpdateStatusAsync()
        {
            try
            {
                _logger.LogDebug("{AxisName} 开始更新状态", AxisName);
                var status = await Task.Run(() => _driver.ReadStatus(_slaveId));

                ActualPosition = status.ActualPosition;
                CommandPosition = status.CommandPosition;
                ActualSpeed = status.ActualSpeed;
                AlarmCode = status.AlarmCode;
                AlarmDescription = status.GetAlarmDescription();
                IsReady = status.IsReady;
                IsMoving = status.IsMoving;

                UpdateStatusText();
                
                _logger.LogDebug("{AxisName} 状态更新完成: 位置={ActualPosition}, 速度={ActualSpeed}, 报警={AlarmDescription}",
                    AxisName, ActualPosition, ActualSpeed, AlarmDescription);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "⚠️ {AxisName} 更新状态失败", AxisName);
                Status = "状态更新失败";
            }
        }

        private void UpdateStatusText()
        {
            if (AlarmCode != 0)
            {
                Status = $"报警: {AlarmDescription}";
                _logger.LogWarning("{AxisName} 状态: {Status}", AxisName, Status);
            }
            else if (IsMoving)
            {
                Status = "运行中";
                _logger.LogInformation("{AxisName} 状态: {Status}", AxisName, Status);
            }
            else if (IsReady)
            {
                Status = "就绪";
                _logger.LogInformation("{AxisName} 状态: {Status}", AxisName, Status);
            }
            else if (IsConnected)
            {
                Status = "已连接";
                _logger.LogInformation("{AxisName} 状态: {Status}", AxisName, Status);
            }
            else
            {
                Status = "未连接";
                _logger.LogInformation("{AxisName} 状态: {Status}", AxisName, Status);
            }
        }

        public void StartMonitoring()
        {
            if (_isMonitoring) 
            {
                _logger.LogInformation("{AxisName} 监控已在运行", AxisName);
                return;
            }

            _isMonitoring = true;
            _monitoringCts = new CancellationTokenSource();

            _logger.LogInformation("📡 {AxisName} 启动状态监控", AxisName);

            Task.Run(async () =>
            {
                while (!_monitoringCts.Token.IsCancellationRequested && _isMonitoring)
                {
                    try
                    {
                        if (IsConnected)
                        {
                            await UpdateStatusAsync();
                        }
                        else
                        {
                            _logger.LogDebug("{AxisName} 未连接,跳过状态更新", AxisName);
                        }
                        
                        await Task.Delay(500, _monitoringCts.Token); // 500ms 更新一次
                    }
                    catch (OperationCanceledException)
                    {
                        _logger.LogInformation("{AxisName} 监控被取消", AxisName);
                        break;
                    }
                    catch (Exception ex)
                    {
                        _logger.LogError(ex, "{AxisName} 监控出错", AxisName);
                        await Task.Delay(1000);
                    }
                }

                _isMonitoring = false;
                _logger.LogInformation("📡 {AxisName} 状态监控已停止", AxisName);
            }, _monitoringCts.Token);
        }

        public void StopMonitoring()
        {
            _logger.LogInformation("📡 {AxisName} 停止状态监控", AxisName);
            _monitoringCts?.Cancel();
            _isMonitoring = false;
        }
        #endregion

        #region 辅助方法
        public AzDriverController GetDriver()
        {
            return _driver;
        }
        #endregion

        #region 清理资源
        public void Dispose()
        {
            if (_isDisposed) 
            {
                _logger.LogDebug("{AxisName} 已释放,跳过", AxisName);
                return;
            }

            _logger.LogInformation("{AxisName} 开始释放资源", AxisName);
            StopMonitoring();
            _monitoringCts?.Dispose();

            _isDisposed = true;
            GC.SuppressFinalize(this);
            
            _logger.LogInformation("{AxisName} 资源已释放", AxisName);
        }

        ~AxisService()
        {
            Dispose();
        }
        #endregion
    }
}

三、视图模型实现

TestViewModel.cs

csharp 复制代码
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Media;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using Microsoft.Extensions.Logging;
using ModbusMultiSlave.Controllers;
using ModbusMultiSlave.Services;

namespace ModbusMultiSlave.ViewModels
{
    /// <summary>
    /// 测试视图模型 - 控制两个轴
    /// </summary>
    public partial class TestViewModel : ObservableObject, IDisposable
    {
        #region 私有字段
        private readonly ILogger<TestViewModel> _logger;
        private readonly IAxisService _xAxisService;
        private readonly IAxisService _rzAxisService;
        
        private readonly SemaphoreSlim _commandLock = new SemaphoreSlim(1, 1);
        private CancellationTokenSource _monitoringCts;
        private bool _isMonitoring = false;
        private bool _isDisposed = false;
        #endregion

        #region 轴控制属性
        // X轴属性
        [ObservableProperty]
        private string _xAxisStatus = "未连接";
        
        [ObservableProperty]
        private bool _xAxisIsConnected = false;
        
        [ObservableProperty]
        private int _xAxisActualPosition;
        
        [ObservableProperty]
        private int _xAxisCommandPosition;
        
        [ObservableProperty]
        private int _xAxisActualSpeed;
        
        [ObservableProperty]
        private string _xAxisAlarm = "正常";
        
        [ObservableProperty]
        private bool _xAxisIsReady;
        
        [ObservableProperty]
        private bool _xAxisIsMoving;
        
        [ObservableProperty]
        private int _xAxisTargetPosition = 0;
        
        [ObservableProperty]
        private int _xAxisTargetSpeed = 1000;
        
        [ObservableProperty]
        private int _xAxisAcceleration = 1500;
        
        [ObservableProperty]
        private int _xAxisDeceleration = 1500;

        // Rz轴属性
        [ObservableProperty]
        private string _rzAxisStatus = "未连接";
        
        [ObservableProperty]
        private bool _rzAxisIsConnected = false;
        
        [ObservableProperty]
        private int _rzAxisActualPosition;
        
        [ObservableProperty]
        private int _rzAxisCommandPosition;
        
        [ObservableProperty]
        private int _rzAxisActualSpeed;
        
        [ObservableProperty]
        private string _rzAxisAlarm = "正常";
        
        [ObservableProperty]
        private bool _rzAxisIsReady;
        
        [ObservableProperty]
        private bool _rzAxisIsMoving;
        
        [ObservableProperty]
        private int _rzAxisTargetPosition = 0;
        
        [ObservableProperty]
        private int _rzAxisTargetSpeed = 1000;
        
        [ObservableProperty]
        private int _rzAxisAcceleration = 1500;
        
        [ObservableProperty]
        private int _rzAxisDeceleration = 1500;

        // 连接参数
        [ObservableProperty]
        private string _comPort = "COM11";
        
        [ObservableProperty]
        private int _baudRate = 115200;
        
        [ObservableProperty]
        private bool _isConnecting = false;
        #endregion

        #region 构造函数
        public TestViewModel(
            ILogger<TestViewModel> logger,
            IAxisService xAxisService,
            IAxisService rzAxisService)
        {
            _logger = logger ?? throw new ArgumentNullException(nameof(logger));
            _xAxisService = xAxisService ?? throw new ArgumentNullException(nameof(xAxisService));
            _rzAxisService = rzAxisService ?? throw new ArgumentNullException(nameof(rzAxisService));
            
            _logger.LogInformation("TestViewModel 初始化完成");
            
            // 初始化连接
            InitializeConnection();
        }
        #endregion

        #region 初始化
        private async void InitializeConnection()
        {
            try
            {
                _logger.LogInformation("开始初始化 AZ 驱动器连接");
                
                // 异步连接两个轴
                await ConnectAllAxesAsync();
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "初始化连接失败");
            }
        }

        private async Task ConnectAllAxesAsync()
        {
            try
            {
                IsConnecting = true;
                
                _logger.LogInformation("=== 第一阶段: 连接 X 轴 ===");
                
                // 连接 X 轴
                bool xConnected = await _xAxisService.ConnectAsync(ComPort, BaudRate);
                XAxisIsConnected = xConnected;
                XAxisStatus = xConnected ? "已连接" : "连接失败";
                
                if (xConnected)
                {
                    await Task.Delay(1000);
                    
                    _logger.LogInformation("=== 第二阶段: 连接 Rz 轴 ===");
                    
                    // 连接 Rz 轴
                    bool rzConnected = await _rzAxisService.ConnectAsync(ComPort, BaudRate);
                    RzAxisIsConnected = rzConnected;
                    RzAxisStatus = rzConnected ? "已连接" : "连接失败";
                    
                    if (rzConnected)
                    {
                        _logger.LogInformation("=== 第三阶段: 启动状态监控 ===");
                        
                        // 启动状态监控
                        _xAxisService.StartMonitoring();
                        _rzAxisService.StartMonitoring();
                        
                        // 启动自己的状态更新循环
                        StartStatusMonitoring();
                        
                        _logger.LogInformation("✅ 所有轴连接成功并启动监控");
                    }
                }
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "连接轴失败");
            }
            finally
            {
                IsConnecting = false;
            }
        }
        #endregion

        #region 状态监控
        private void StartStatusMonitoring()
        {
            if (_isMonitoring) return;

            _isMonitoring = true;
            _monitoringCts = new CancellationTokenSource();

            Task.Run(async () =>
            {
                while (!_monitoringCts.Token.IsCancellationRequested && _isMonitoring)
                {
                    try
                    {
                        await UpdateAxesStatusAsync();
                        await Task.Delay(500, _monitoringCts.Token);
                    }
                    catch (OperationCanceledException)
                    {
                        break;
                    }
                    catch (Exception ex)
                    {
                        _logger.LogError(ex, "状态监控出错");
                        await Task.Delay(1000);
                    }
                }
            }, _monitoringCts.Token);
        }

        private void StopStatusMonitoring()
        {
            _monitoringCts?.Cancel();
            _isMonitoring = false;
        }

        private async Task UpdateAxesStatusAsync()
        {
            // 使用信号量确保串行更新
            await _commandLock.WaitAsync();
            try
            {
                // 更新 X 轴状态
                if (XAxisIsConnected)
                {
                    await _xAxisService.UpdateStatusAsync();
                    
                    XAxisActualPosition = _xAxisService.ActualPosition;
                    XAxisCommandPosition = _xAxisService.CommandPosition;
                    XAxisActualSpeed = _xAxisService.ActualSpeed;
                    XAxisAlarm = _xAxisService.AlarmDescription;
                    XAxisIsReady = _xAxisService.IsReady;
                    XAxisIsMoving = _xAxisService.IsMoving;
                    XAxisStatus = _xAxisService.Status;
                }

                // 短暂延迟,避免通信冲突
                await Task.Delay(50);

                // 更新 Rz 轴状态
                if (RzAxisIsConnected)
                {
                    await _rzAxisService.UpdateStatusAsync();
                    
                    RzAxisActualPosition = _rzAxisService.ActualPosition;
                    RzAxisCommandPosition = _rzAxisService.CommandPosition;
                    RzAxisActualSpeed = _rzAxisService.ActualSpeed;
                    RzAxisAlarm = _rzAxisService.AlarmDescription;
                    RzAxisIsReady = _rzAxisService.IsReady;
                    RzAxisIsMoving = _rzAxisService.IsMoving;
                    RzAxisStatus = _rzAxisService.Status;
                }
            }
            finally
            {
                _commandLock.Release();
            }
        }
        #endregion

        #region X轴命令
        [RelayCommand]
        private async Task XAxisConnectAsync()
        {
            try
            {
                XAxisStatus = "连接中...";
                var result = await _xAxisService.ConnectAsync(ComPort, BaudRate);
                XAxisIsConnected = result;
                XAxisStatus = result ? "已连接" : "连接失败";

                if (result)
                {
                    _xAxisService.StartMonitoring();
                }
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "X轴连接失败");
                XAxisStatus = "连接异常";
            }
        }

        [RelayCommand]
        private async Task XAxisDisconnectAsync()
        {
            try
            {
                await _xAxisService.DisconnectAsync();
                XAxisIsConnected = false;
                XAxisStatus = "未连接";
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "X轴断开失败");
            }
        }

        [RelayCommand]
        private async Task XAxisSetOriginAsync()
        {
            try
            {
                if (!XAxisIsConnected) return;

                var result = await _xAxisService.SetOriginAsync();
                if (!result)
                {
                    await ShowErrorAsync("X轴原点设置", "设置失败");
                }
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "X轴原点设置失败");
                await ShowErrorAsync("X轴原点设置", ex.Message);
            }
        }

        [RelayCommand]
        private async Task XAxisGoHomeAsync()
        {
            try
            {
                if (!XAxisIsConnected) return;

                var result = await _xAxisService.GoHomeAsync();
                if (!result)
                {
                    await ShowErrorAsync("X轴回原点", "执行失败");
                }
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "X轴回原点失败");
                await ShowErrorAsync("X轴回原点", ex.Message);
            }
        }

        [RelayCommand]
        private async Task XAxisStartAsync()
        {
            try
            {
                if (!XAxisIsConnected) return;

                // 更新驱动器参数
                await _xAxisService.SetSpeedAsync(XAxisTargetSpeed);
                _xAxisService.TargetPosition = XAxisTargetPosition;

                // 执行绝对定位
                var result = await _xAxisService.MoveAbsoluteAsync();
                if (!result)
                {
                    await ShowErrorAsync("X轴启动", "启动失败");
                }
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "X轴启动失败");
                await ShowErrorAsync("X轴启动", ex.Message);
            }
        }

        [RelayCommand]
        private async Task XAxisStopAsync()
        {
            try
            {
                if (!XAxisIsConnected) return;

                var result = await _xAxisService.StopAsync();
                if (!result)
                {
                    await ShowErrorAsync("X轴停止", "停止失败");
                }
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "X轴停止失败");
                await ShowErrorAsync("X轴停止", ex.Message);
            }
        }

        [RelayCommand]
        private async Task XAxisResetAlarmAsync()
        {
            try
            {
                if (!XAxisIsConnected) return;

                var result = await _xAxisService.ResetAlarmAsync();
                if (!result)
                {
                    await ShowErrorAsync("X轴清理报警", "清理失败");
                }
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "X轴清理报警失败");
                await ShowErrorAsync("X轴清理报警", ex.Message);
            }
        }

        [RelayCommand]
        private async Task XAxisEmergencyStopAsync()
        {
            try
            {
                if (!XAxisIsConnected) return;

                var result = await _xAxisService.EmergencyStopAsync();
                if (!result)
                {
                    await ShowErrorAsync("X轴急停", "急停失败");
                }
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "X轴急停失败");
                await ShowErrorAsync("X轴急停", ex.Message);
            }
        }

        [RelayCommand]
        private async Task XAxisSetSpeedAsync()
        {
            try
            {
                if (!XAxisIsConnected) return;

                var result = await _xAxisService.SetSpeedAsync(XAxisTargetSpeed);
                if (!result)
                {
                    await ShowErrorAsync("X轴设置速度", "设置失败");
                }
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "X轴设置速度失败");
                await ShowErrorAsync("X轴设置速度", ex.Message);
            }
        }
        #endregion

        #region Rz轴命令
        [RelayCommand]
        private async Task RzAxisConnectAsync()
        {
            try
            {
                RzAxisStatus = "连接中...";
                var result = await _rzAxisService.ConnectAsync(ComPort, BaudRate);
                RzAxisIsConnected = result;
                RzAxisStatus = result ? "已连接" : "连接失败";

                if (result)
                {
                    _rzAxisService.StartMonitoring();
                }
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Rz轴连接失败");
                RzAxisStatus = "连接异常";
            }
        }

        [RelayCommand]
        private async Task RzAxisDisconnectAsync()
        {
            try
            {
                await _rzAxisService.DisconnectAsync();
                RzAxisIsConnected = false;
                RzAxisStatus = "未连接";
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Rz轴断开失败");
            }
        }

        [RelayCommand]
        private async Task RzAxisSetOriginAsync()
        {
            try
            {
                if (!RzAxisIsConnected) return;

                var result = await _rzAxisService.SetOriginAsync();
                if (!result)
                {
                    await ShowErrorAsync("Rz轴原点设置", "设置失败");
                }
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Rz轴原点设置失败");
                await ShowErrorAsync("Rz轴原点设置", ex.Message);
            }
        }

        [RelayCommand]
        private async Task RzAxisGoHomeAsync()
        {
            try
            {
                if (!RzAxisIsConnected) return;

                var result = await _rzAxisService.GoHomeAsync();
                if (!result)
                {
                    await ShowErrorAsync("Rz轴回原点", "执行失败");
                }
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Rz轴回原点失败");
                await ShowErrorAsync("Rz轴回原点", ex.Message);
            }
        }

        [RelayCommand]
        private async Task RzAxisStartAsync()
        {
            try
            {
                if (!RzAxisIsConnected) return;

                await _rzAxisService.SetSpeedAsync(RzAxisTargetSpeed);
                _rzAxisService.TargetPosition = RzAxisTargetPosition;

                var result = await _rzAxisService.MoveAbsoluteAsync();
                if (!result)
                {
                    await ShowErrorAsync("Rz轴启动", "启动失败");
                }
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Rz轴启动失败");
                await ShowErrorAsync("Rz轴启动", ex.Message);
            }
        }

        [RelayCommand]
        private async Task RzAxisStopAsync()
        {
            try
            {
                if (!RzAxisIsConnected) return;

                var result = await _rzAxisService.StopAsync();
                if (!result)
                {
                    await ShowErrorAsync("Rz轴停止", "停止失败");
                }
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Rz轴停止失败");
                await ShowErrorAsync("Rz轴停止", ex.Message);
            }
        }

        [RelayCommand]
        private async Task RzAxisResetAlarmAsync()
        {
            try
            {
                if (!RzAxisIsConnected) return;

                var result = await _rzAxisService.ResetAlarmAsync();
                if (!result)
                {
                    await ShowErrorAsync("Rz轴清理报警", "清理失败");
                }
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Rz轴清理报警失败");
                await ShowErrorAsync("Rz轴清理报警", ex.Message);
            }
        }

        [RelayCommand]
        private async Task RzAxisEmergencyStopAsync()
        {
            try
            {
                if (!RzAxisIsConnected) return;

                var result = await _rzAxisService.EmergencyStopAsync();
                if (!result)
                {
                    await ShowErrorAsync("Rz轴急停", "急停失败");
                }
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Rz轴急停失败");
                await ShowErrorAsync("Rz轴急停", ex.Message);
            }
        }

        [RelayCommand]
        private async Task RzAxisSetSpeedAsync()
        {
            try
            {
                if (!RzAxisIsConnected) return;

                var result = await _rzAxisService.SetSpeedAsync(RzAxisTargetSpeed);
                if (!result)
                {
                    await ShowErrorAsync("Rz轴设置速度", "设置失败");
                }
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Rz轴设置速度失败");
                await ShowErrorAsync("Rz轴设置速度", ex.Message);
            }
        }
        #endregion

        #region 双轴控制
        [RelayCommand]
        private async Task ConnectAllAxesCommandAsync()
        {
            await ConnectAllAxesAsync();
        }

        [RelayCommand]
        private async Task DisconnectAllAxesAsync()
        {
            try
            {
                await _xAxisService.DisconnectAsync();
                await _rzAxisService.DisconnectAsync();
                
                XAxisIsConnected = false;
                RzAxisIsConnected = false;
                XAxisStatus = "未连接";
                RzAxisStatus = "未连接";
                
                StopStatusMonitoring();
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "断开所有轴失败");
            }
        }

        [RelayCommand]
        private async Task EmergencyStopAllAsync()
        {
            try
            {
                if (XAxisIsConnected)
                    await _xAxisService.EmergencyStopAsync();
                
                await Task.Delay(50);
                
                if (RzAxisIsConnected)
                    await _rzAxisService.EmergencyStopAsync();
                
                _logger.LogInformation("所有轴急停完成");
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "急停所有轴失败");
            }
        }
        #endregion

        #region 诊断命令
        [RelayCommand]
        private async Task DiagnoseCommunicationAsync()
        {
            try
            {
                _logger.LogInformation("=== 开始通信诊断 ===");
                
                // 1. 检查串口
                var ports = System.IO.Ports.SerialPort.GetPortNames();
                _logger.LogInformation("可用串口: {Ports}", string.Join(", ", ports));
                
                // 2. 测试 X 轴通信
                if (XAxisIsConnected)
                {
                    _logger.LogInformation("测试 X 轴通信...");
                    
                    // 诊断位置数据
                    await Task.Run(() => 
                        _xAxisService.GetDriver().Diagnose32BitData(1, AzDriverAddresses.ActualPosition, 20000));
                    
                    await Task.Delay(200);
                }
                
                // 3. 测试 Rz 轴通信
                if (RzAxisIsConnected)
                {
                    await Task.Delay(100);
                    _logger.LogInformation("测试 Rz 轴通信...");
                    
                    await Task.Run(() => 
                        _rzAxisService.GetDriver().Diagnose32BitData(2, AzDriverAddresses.ActualPosition, 20000));
                }
                
                _logger.LogInformation("=== 通信诊断结束 ===");
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "通信诊断失败");
            }
        }

        [RelayCommand]
        private async Task Test32BitDataConversionAsync()
        {
            try
            {
                _logger.LogInformation("=== 测试 32 位数据转换 ===");
                
                // 测试值
                int[] testValues = { 1000, 20000, -1000, -16000, 32767, -32768, 100000, -100000 };
                
                foreach (var value in testValues)
                {
                    _logger.LogInformation("测试值: {Value}", value);
                    
                    // 测试 X 轴
                    if (XAxisIsConnected)
                    {
                        // 写入目标位置
                        bool writeResult = await Task.Run(() => 
                            _xAxisService.GetDriver().Write32BitData(1, AzDriverAddresses.TargetPosition, value));
                        
                        if (writeResult)
                        {
                            await Task.Delay(100);
                            
                            // 读取回来验证
                            int readValue = await Task.Run(() => 
                                _xAxisService.GetDriver().Read32BitData(1, AzDriverAddresses.TargetPosition));
                            
                            bool match = value == readValue;
                            _logger.LogInformation("X轴写入/读取: {Match}, 写入={Write}, 读取={Read}", 
                                match ? "✅" : "❌", value, readValue);
                        }
                    }
                    
                    await Task.Delay(200);
                }
                
                _logger.LogInformation("=== 数据转换测试结束 ===");
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "数据转换测试失败");
            }
        }
        #endregion

        #region 辅助方法
        private async Task ShowErrorAsync(string operation, string message)
        {
            await Application.Current.Dispatcher.InvokeAsync(() =>
            {
                MessageBox.Show(
                    $"{operation}: {message}",
                    "错误",
                    MessageBoxButton.OK,
                    MessageBoxImage.Error
                );
            });
        }
        #endregion

        #region 清理资源
        public void Dispose()
        {
            if (_isDisposed) return;

            _logger.LogInformation("开始释放 TestViewModel 资源");
            
            StopStatusMonitoring();
            _monitoringCts?.Dispose();
            
            _xAxisService?.StopMonitoring();
            _rzAxisService?.StopMonitoring();
            
            _isDisposed = true;
            GC.SuppressFinalize(this);
            
            _logger.LogInformation("TestViewModel 资源已释放");
        }

        ~TestViewModel()
        {
            Dispose();
        }
        #endregion
    }
}

四、应用入口和依赖注入配置

App.xaml.cs

csharp 复制代码
using System;
using System.IO;
using System.Runtime.InteropServices;
using System.Windows;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using ModbusMultiSlave.Controllers;
using ModbusMultiSlave.Services;
using ModbusMultiSlave.ViewModels;

namespace ModbusMultiSlave
{
    public partial class App : Application
    {
        [DllImport("kernel32.dll")]
        private static extern bool AllocConsole();

        public static IServiceProvider ServiceProvider { get; private set; }

        protected override void OnStartup(StartupEventArgs e)
        {
            AllocConsole();  // 显示控制台窗口,方便调试

            var services = new ServiceCollection();

            // 配置日志
            services.AddLogging(loggingBuilder =>
            {
                loggingBuilder.SetMinimumLevel(LogLevel.Debug);
                loggingBuilder.AddConsole();
                loggingBuilder.AddProvider(new CustomLoggerProvider(
                    Path.Combine(Environment.CurrentDirectory, "modbus.log")));
            });

            // 注册核心服务(单例)
            services.AddSingleton<AzDriverController>();
            
            // 注册轴服务(X轴 - 从站1,Rz轴 - 从站2)
            services.AddSingleton<IAxisService>(provider =>
            {
                var driver = provider.GetRequiredService<AzDriverController>();
                var logger = provider.GetRequiredService<ILogger<AxisService>>();
                return new AxisService(1, "X轴", logger, driver);
            });
            
            services.AddSingleton<IAxisService>(provider =>
            {
                var driver = provider.GetRequiredService<AzDriverController>();
                var logger = provider.GetRequiredService<ILogger<AxisService>>();
                return new AxisService(2, "Rz轴", logger, driver);
            });

            // 注册 ViewModel
            services.AddSingleton<TestViewModel>();
            
            // 如果需要主窗口,可以这样注册
            // services.AddSingleton<MainWindow>();

            ServiceProvider = services.BuildServiceProvider();

            // 创建并显示主窗口
            var mainWindow = new MainWindow
            {
                DataContext = ServiceProvider.GetRequiredService<TestViewModel>()
            };
            
            mainWindow.Show();

            base.OnStartup(e);
        }
    }
}

五、关键问题和解决方案

5.1 问题1:一个COM口多个连接

问题描述 :尝试为每个轴创建独立的 SerialPort 实例连接同一个 COM 口。

解决方案 :使用单例模式的 AzDriverController 共享连接。

5.2 问题2:通信时序冲突

问题描述:两个轴同时通信导致 Modbus 数据帧冲突。

解决方案

  1. 使用 lock 语句确保同一时间只有一个通信操作
  2. 不同从站之间添加最小通信间隔(50ms)
  3. ViewModel 中使用信号量串行化状态更新

5.3 问题3:32位寄存器转换错误

问题描述:寄存器值 65535 和 49536 被错误解析。

解决方案

  1. 正确处理有符号32位整数(二进制补码)
  2. 添加详细的诊断日志
  3. 支持不同数据格式解析

5.4 问题4:控制命令需要点动操作

问题描述:AZ 驱动器控制字需要写命令后清零。

解决方案 :在 SendControlCommand 方法中实现写命令 → 延迟 → 清零的序列。

六、使用说明

6.1 安装依赖

xml 复制代码
<!-- 在 .csproj 文件中添加 -->
<PackageReference Include="NModbus" Version="3.0.90" />
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.2.1" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="8.0.0" />

6.2 配置硬件

  1. 将两个 AZ 驱动器连接到同一个 RS485 总线
  2. 设置不同的从站地址(X轴=1,Rz轴=2)
  3. 确保所有设备使用相同的波特率、数据位、停止位、校验位

6.3 运行应用

  1. 编译并运行应用程序
  2. 观察控制台输出,查看通信日志
  3. 使用界面按钮测试各个功能

七、性能优化建议

7.1 批量读取优化

csharp 复制代码
/// <summary>
/// 批量读取多个寄存器
/// </summary>
public ushort[] ReadMultipleRegisters(byte slaveId, ushort startAddress, ushort count)
{
    lock (_lock)
    {
        try
        {
            return _master.ReadHoldingRegisters(slaveId, (ushort)(startAddress - 1), count);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "批量读取寄存器失败");
            return Array.Empty<ushort>();
        }
    }
}

7.2 异步优化

csharp 复制代码
/// <summary>
/// 异步读取状态(避免阻塞 UI)
/// </summary>
public async Task<AzDriverStatus> ReadStatusAsync(byte slaveId)
{
    return await Task.Run(() => ReadStatus(slaveId));
}

八、常见问题排查

8.1 通信超时

  1. 检查串口参数是否与硬件匹配
  2. 增加 ReadTimeoutWriteTimeout
  3. 检查物理连接是否正常

8.2 从站无响应

  1. 确认从站地址设置正确
  2. 检查 RS485 终端电阻是否正确连接
  3. 使用 Modbus 调试工具验证通信

8.3 寄存器值错误

  1. 使用诊断工具查看原始寄存器值
  2. 检查数据格式(有符号/无符号,高位/低位)
  3. 验证驱动器手册中的寄存器定义

九、总结

本文详细介绍了如何使用 C# 和 NModbus 库实现一个 COM 口控制多个 Modbus RTU 从站。关键点包括:

  1. 共享连接设计:单例模式管理串口连接
  2. 通信时序控制:锁和间隔确保通信不冲突
  3. 数据格式处理:正确解析32位有符号整数
  4. 详细日志:便于调试和问题排查
  5. 依赖注入:清晰的服务管理和生命周期控制

这个解决方案已经过实际项目验证,可以直接用于生产环境。希望对您的项目有所帮助!


相关推荐
hashiqimiya2 小时前
java程序的并发
java·开发语言·python
微露清风2 小时前
系统性学习C++进阶-第十四讲-二叉搜索树
开发语言·c++·学习
董世昌412 小时前
强制类型转换和隐式类型转换的区别
开发语言
Fruiticecake2 小时前
Markdown,不用鼠标也能做笔记!
开发语言
TypingLearn2 小时前
2026年,让.NET再次伟大
windows·c#·.net·sdk·netcore
ulias2122 小时前
多态理论与实践
java·开发语言·前端·c++·算法
幽络源小助理2 小时前
下载安装AndroidStudio配置Gradle运行第一个kotlin程序
android·开发语言·kotlin
蹦蹦跳跳真可爱5892 小时前
Python----大模型(GPT-2模型训练,预测)
开发语言·人工智能·pytorch·python·gpt·深度学习·embedding
ServBay3 小时前
.NET 10 与 C# 14 更新速览,代码更少,性能更好
后端·c#·.net