using DeviceCommand.Base; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace BenchMovementModule.HardwareDrive { public abstract class GantryControlBase { protected readonly IModbusDevice _device; protected readonly byte _slaveAddress; // 默认 PLC Modbus 映射地址 protected const ushort ADDR_HM_X_ENABLE = 0xC100; // HM0: X轴使能 protected const ushort ADDR_HM_Y_ENABLE = 0xC101; // HM1: Y轴使能 protected const ushort ADDR_HM_Z_ENABLE = 0xC102; // HM2: Z轴使能 protected const ushort ADDR_M_X_START = 30; // M30: X轴指定位置启动 protected const ushort ADDR_M_Y_START = 31; // M31: Y轴指定位置启动 protected const ushort ADDR_M_Z_START = 35; // M35: Z轴指定位置启动 protected const ushort ADDR_M_ALL_STOP = 40; // M40: 全停 protected const ushort ADDR_M_X_STOP = 41; // M41: X轴停止 protected const ushort ADDR_M_Y_STOP = 42; // M42: Y轴停止 protected const ushort ADDR_M_Z_STOP = 43; // M43: Z轴停止 protected const ushort ADDR_M_HOME = 100; // M100: 回原点触发 protected const ushort ADDR_HD_X_SPEED = 0xA166; // HD230: X轴速度 (32位) protected const ushort ADDR_HD_Y_SPEED = 0xA17A; // HD250: Y轴速度 (32位) protected const ushort ADDR_HD_Z_SPEED = 0xA170; // HD240: Z轴速度 (32位) protected const ushort ADDR_HD_X_TARGET = 0xA10C; // HD140: X轴指定位置 (32位) protected const ushort ADDR_HD_Y_TARGET = 0xA120; // HD160: Y轴指定位置 (32位) protected const ushort ADDR_HD_Z_TARGET = 0xA116; // HD150: Z轴指定位置 (32位) public bool IsConnected => _device?.IsConnected ?? false; protected GantryControlBase(IModbusDevice device, byte slaveAddress = 1) { _device = device ?? throw new ArgumentNullException(nameof(device)); _slaveAddress = slaveAddress; } public async Task ConnectAsync(CancellationToken ct = default) { return await _device.ConnectAsync(ct); } public void Close() { _device.Close(); } /// /// 轴使能控制 /// public async Task SetAxisEnableAsync(int axis, bool enable, CancellationToken ct = default) { ushort address = axis switch { 1 => ADDR_HM_X_ENABLE, // X 2 => ADDR_HM_Y_ENABLE, // Y 3 => ADDR_HM_Z_ENABLE, // Z _ => throw new ArgumentException("轴通道错误,仅支持 1(X), 2(Y), 3(Z)") }; await _device.WriteSingleCoilAsync(_slaveAddress, address, enable, ct); } /// /// 左右移动(X轴) /// /// 目标绝对位置(脉冲数/单位值) /// 运行速度 public async Task MoveLeftRightAsync(int position, int speed, CancellationToken ct = default) { // 1. 写入速度与目标位置(32位数据,写入连续的2个保持寄存器) await _device.WriteMultipleRegistersAsync(_slaveAddress, ADDR_HD_X_SPEED, Int32ToUshorts(speed), ct); await _device.WriteMultipleRegistersAsync(_slaveAddress, ADDR_HD_X_TARGET, Int32ToUshorts(position), ct); // 2. 边缘触发启动信号 M30 await TriggerCoilAsync(ADDR_M_X_START, ct); } /// /// 上下移动(Z轴) /// /// 目标绝对位置 /// 运行速度 public async Task MoveUpDownAsync(int position, int speed, CancellationToken ct = default) { // 1. 写入速度与目标位置(32位数据) await _device.WriteMultipleRegistersAsync(_slaveAddress, ADDR_HD_Z_SPEED, Int32ToUshorts(speed), ct); await _device.WriteMultipleRegistersAsync(_slaveAddress, ADDR_HD_Z_TARGET, Int32ToUshorts(position), ct); // 2. 边缘触发启动信号 M35 await TriggerCoilAsync(ADDR_M_Z_START, ct); } /// /// 前后移动(Y轴 - 预留拓展) /// public async Task MoveFrontBackAsync(int position, int speed, CancellationToken ct = default) { await _device.WriteMultipleRegistersAsync(_slaveAddress, ADDR_HD_Y_SPEED, Int32ToUshorts(speed), ct); await _device.WriteMultipleRegistersAsync(_slaveAddress, ADDR_HD_Y_TARGET, Int32ToUshorts(position), ct); await TriggerCoilAsync(ADDR_M_Y_START, ct); } /// /// 全局回零 /// public async Task HomeAsync(CancellationToken ct = default) { await TriggerCoilAsync(ADDR_M_HOME, ct); } /// /// 轴停止 /// /// 1:X轴, 2:Y轴, 3:Z轴, 0:全停 public async Task StopAsync(int axis = 0, CancellationToken ct = default) { ushort address = axis switch { 0 => ADDR_M_ALL_STOP, 1 => ADDR_M_X_STOP, 2 => ADDR_M_Y_STOP, 3 => ADDR_M_Z_STOP, _ => ADDR_M_ALL_STOP }; await TriggerCoilAsync(address, ct); } #region 内部工具方法 /// /// 触发一个点动信号(置 1 后,延时 100ms 自动置 0) /// protected async Task TriggerCoilAsync(ushort address, CancellationToken ct = default) { await _device.WriteSingleCoilAsync(_slaveAddress, address, true, ct); await Task.Delay(100, ct); // 给予 PLC 扫描周期足够的响应时间 await _device.WriteSingleCoilAsync(_slaveAddress, address, false, ct); } /// /// 将 32 位整型数据转换为 Modbus 的 2 个 16 位无符号整数(高低字转换) /// protected ushort[] Int32ToUshorts(int value) { // 本处采用低字在前(CDAB),如果PLC高字在前(ABCD),可将返回数组顺序颠倒 ushort lowWord = (ushort)(value & 0xFFFF); ushort highWord = (ushort)((value >> 16) & 0xFFFF); return new ushort[] { lowWord, highWord }; } #endregion } }