现场优化4

This commit is contained in:
2026-08-15 18:43:38 +08:00
parent 8d941d92f0
commit 92eab2f626
5 changed files with 249 additions and 31 deletions
@@ -287,7 +287,7 @@ namespace BenchMovementModule.ViewModels
if (!EnsureConnected()) return; if (!EnsureConnected()) return;
try try
{ {
await _gantry!.(); await _gantry!.(15);
LoggerHelper.Info("[BenchMovement] 回原点已触发"); LoggerHelper.Info("[BenchMovement] 回原点已触发");
} }
catch (Exception ex) catch (Exception ex)
+2 -2
View File
@@ -12,8 +12,8 @@ namespace DeviceCommand.Base
{ {
public string IPAddress { get; private set; } = "127.0.0.1"; public string IPAddress { get; private set; } = "127.0.0.1";
public int Port { get; private set; } = 502; public int Port { get; private set; } = 502;
public int SendTimeout { get; private set; } = 3000; public int SendTimeout { get; private set; } = 10000;
public int ReceiveTimeout { get; private set; } = 3000; public int ReceiveTimeout { get; private set; } = 10000;
private TcpClient _tcpClient; private TcpClient _tcpClient;
public IModbusMaster Modbus { get; private set; } public IModbusMaster Modbus { get; private set; }
+162 -24
View File
@@ -7,6 +7,7 @@ using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Threading; using System.Threading;
using System.ComponentModel;
namespace DeviceCommand.Devices namespace DeviceCommand.Devices
{ {
@@ -18,6 +19,14 @@ namespace DeviceCommand.Devices
// 向后兼容:TcpDevice 即自身(继承自 ModbusTcp // 向后兼容:TcpDevice 即自身(继承自 ModbusTcp
public ModbusTcp TcpDevice => this; public ModbusTcp TcpDevice => this;
// =================================================================
// ------------------ 软件位置跟踪(绝对位置寄存器会被清零)----------
// =================================================================
/// <summary>当前位置相对于机械零点的位置(每次移动后累加偏移)</summary>
private int _originX, _originY, _originZ;
/// <summary>是否已完成回零初始化</summary>
private bool _isHomed;
public GantryControlTcp(TcpConfig config) : base(config) public GantryControlTcp(TcpConfig config) : base(config)
{ {
@@ -341,17 +350,70 @@ namespace DeviceCommand.Devices
} }
/// <summary> /// <summary>
/// 全局回零(安全校验版:先检查回原点速度是否为 0,为0则拒绝触发) /// 全局回零(安全校验版:先检查回原点速度是否为 0,为0则拒绝触发)
/// 回零完成后读取绝对位置寄存器保存为初始值,重置累计偏移。
/// </summary> /// </summary>
/// <param name="回零点等待时间">回零点等待时间</param>
/// <param name="取消令牌">取消令牌</param> /// <param name="取消令牌">取消令牌</param>
public async Task (CancellationToken = default) public async Task (int , CancellationToken = default)
{ {
int currentSpeed = await (); int currentSpeed = await ();
if (currentSpeed == 0) if (currentSpeed == 0)
throw new InvalidOperationException("无法触发回原点!当前回原点定位速度为 0,请先设置合法的回零速度。"); throw new InvalidOperationException("无法触发回原点!当前回原点定位速度为 0,请先设置合法的回零速度。");
// 1. 触发回零
await TriggerCoilAsync(ADDR_M_HOME_TRIGGER, ); await TriggerCoilAsync(ADDR_M_HOME_TRIGGER, );
// 2. 等待到达机械零点(绝对位置 = 0)
await (, );
await Task.Delay(500, );
// 3. 清零绝对位置寄存器
await WriteMultipleRegistersAsync(_slaveAddress, ADDR_ABS_AXIS1_POS, Int32ToUshorts(0), );
await WriteMultipleRegistersAsync(_slaveAddress, ADDR_ABS_AXIS2_POS, Int32ToUshorts(0), );
await WriteMultipleRegistersAsync(_slaveAddress, ADDR_ABS_AXIS3_POS, Int32ToUshorts(0), );
// 4. 设置当前位置为机械零点 (0, 0, 0)
// 设置目标位置
await WriteMultipleRegistersAsync(_slaveAddress, ADDR_HD_X_TARGET, Int32ToUshorts(0), );
await WriteMultipleRegistersAsync(_slaveAddress, ADDR_HD_Y_TARGET, Int32ToUshorts(0), );
await WriteMultipleRegistersAsync(_slaveAddress, ADDR_HD_Z_TARGET, Int32ToUshorts(0), );
// 5. 读取配置的待定位置(原点位置)
var (xHome, yHome, zHome) = await ();
// 6. 移动到待定位置
if (xHome != 0 || yHome != 0 || zHome != 0)
{
_originX = xHome;
_originY = yHome;
_originZ = zHome;
// 启动三轴联动
await ();
// 等待到达待定位置
await (xHome, yHome, zHome, , );
// 7. 到达后清零绝对位置寄存器
await Task.Delay(500, );
await WriteMultipleRegistersAsync(_slaveAddress, ADDR_ABS_AXIS1_POS, Int32ToUshorts(0), );
await WriteMultipleRegistersAsync(_slaveAddress, ADDR_ABS_AXIS2_POS, Int32ToUshorts(0), );
await WriteMultipleRegistersAsync(_slaveAddress, ADDR_ABS_AXIS3_POS, Int32ToUshorts(0), );
// 清零目标寄存器
await WriteMultipleRegistersAsync(_slaveAddress, ADDR_HD_X_TARGET, Int32ToUshorts(0), );
await WriteMultipleRegistersAsync(_slaveAddress, ADDR_HD_Y_TARGET, Int32ToUshorts(0), );
await WriteMultipleRegistersAsync(_slaveAddress, ADDR_HD_Z_TARGET, Int32ToUshorts(0), );
}
// 8. 更新当前位置为待定位置
_originX = xHome;
_originY = yHome;
_originZ = zHome;
_isHomed = true;
} }
/// <summary> /// <summary>
@@ -467,22 +529,6 @@ namespace DeviceCommand.Devices
int yTarget = await (2, ); int yTarget = await (2, );
int zTarget = await (3, ); int zTarget = await (3, );
// X轴安全范围联动校验
int xMaxAllowed = 50000 - xHome;
if (xTarget < 0 || xTarget > xMaxAllowed)
throw new ArgumentOutOfRangeException(nameof(xTarget), $"X轴目标位置 [{xTarget}] 越界,当前原点下最大范围 0-{xMaxAllowed}");
// Y轴安全范围联动校验
int yMaxAllowed = 50000 - yHome;
if (yTarget < 0 || yTarget > yMaxAllowed)
throw new ArgumentOutOfRangeException(nameof(yTarget), $"Y轴目标位置 [{yTarget}] 越界,当前原点下最大范围 0-{yMaxAllowed}");
// Z轴安全范围联动校验
int zMaxAllowed = 20000 - zHome;
if (zTarget < 0 || zTarget > zMaxAllowed)
throw new ArgumentOutOfRangeException(nameof(zTarget), $"Z轴目标位置 [{zTarget}] 越界,当前原点下最大范围 0-{zMaxAllowed}");
// 4. 全部联动校验通过,三轴点动同步启动
await WriteSingleCoilAsync(_slaveAddress, ADDR_M_X_START, true, ); await WriteSingleCoilAsync(_slaveAddress, ADDR_M_X_START, true, );
await WriteSingleCoilAsync(_slaveAddress, ADDR_M_Y_START, true, ); await WriteSingleCoilAsync(_slaveAddress, ADDR_M_Y_START, true, );
await WriteSingleCoilAsync(_slaveAddress, ADDR_M_Z_START, true, ); await WriteSingleCoilAsync(_slaveAddress, ADDR_M_Z_START, true, );
@@ -494,6 +540,68 @@ namespace DeviceCommand.Devices
await WriteSingleCoilAsync(_slaveAddress, ADDR_M_Z_START, false, ); await WriteSingleCoilAsync(_slaveAddress, ADDR_M_Z_START, false, );
} }
/// <summary>
/// 三轴相对移动:以(初始绝对值 + 累计偏移 + 本次偏移)做越界校验,
/// 清零绝对位置寄存器后将偏移量写入目标寄存器,启动三轴联动,
/// 等待到达后清零目标寄存器并更新累计偏移。偏移量可为负值。
/// 调用前必须先执行回零()完成初始化。
/// </summary>
/// <param name="X偏移量">X轴相对偏移量(可为负)</param>
/// <param name="Y偏移量">Y轴相对偏移量(可为负)</param>
/// <param name="Z偏移量">Z轴相对偏移量(可为负)</param>
/// <param name="超时秒数">等待到达目标位置的最大超时时间(秒)</param>
/// <param name="取消令牌">取消令牌</param>
public async Task (int X偏移量, int Y偏移量, int Z偏移量, int = 60, CancellationToken = default)
{
if (!_isHomed)
throw new InvalidOperationException("尚未完成回零初始化,请先调用 回零() 方法。");
// 1. 越界校验:当前位置 + 本次偏移是否在合法范围
int targetAbsX = _originX + X偏移量;
int targetAbsY = _originY + Y偏移量;
int targetAbsZ = _originZ + Z偏移量;
if (targetAbsX > 50000)
throw new ArgumentOutOfRangeException(nameof(X偏移量),
$"X轴越界:当前 {_originX} + 偏移 {X偏移量} = {targetAbsX},最大范围 50000");
if (targetAbsY > 50000)
throw new ArgumentOutOfRangeException(nameof(Y偏移量),
$"Y轴越界:当前 {_originY} + 偏移 {Y偏移量} = {targetAbsY},最大范围 50000");
if (targetAbsZ > 20000)
throw new ArgumentOutOfRangeException(nameof(Z偏移量),
$"Z轴越界:当前 {_originZ} + 偏移 {Z偏移量} = {targetAbsZ},最大范围 20000");
// 2. 将偏移量写入目标位置寄存器(ABS已在上次移动后清零,目标 = 偏移量)
await WriteMultipleRegistersAsync(_slaveAddress, ADDR_HD_X_TARGET, Int32ToUshorts(X偏移量), );
await WriteMultipleRegistersAsync(_slaveAddress, ADDR_HD_Y_TARGET, Int32ToUshorts(Y偏移量), );
await WriteMultipleRegistersAsync(_slaveAddress, ADDR_HD_Z_TARGET, Int32ToUshorts(Z偏移量), );
// 3. 启动三轴联动
await ();
await ();
await ();
// 5. 到达后更新当前位置(累加偏移)
_originX += X偏移量;
_originY += Y偏移量;
_originZ += Z偏移量;
}
public async Task ( CancellationToken = default)
{
// 6. 清零 6 轴绝对位置寄存器
await WriteMultipleRegistersAsync(_slaveAddress, ADDR_ABS_AXIS1_POS, Int32ToUshorts(0), );
await WriteMultipleRegistersAsync(_slaveAddress, ADDR_ABS_AXIS2_POS, Int32ToUshorts(0), );
await WriteMultipleRegistersAsync(_slaveAddress, ADDR_ABS_AXIS3_POS, Int32ToUshorts(0), );
await WriteMultipleRegistersAsync(_slaveAddress, ADDR_ABS_AXIS4_POS, Int32ToUshorts(0), );
await WriteMultipleRegistersAsync(_slaveAddress, ADDR_ABS_AXIS5_POS, Int32ToUshorts(0), );
await WriteMultipleRegistersAsync(_slaveAddress, ADDR_ABS_AXIS6_POS, Int32ToUshorts(0), );
}
#region #region
/// <summary> /// <summary>
@@ -582,12 +690,12 @@ namespace DeviceCommand.Devices
#endregion #endregion
#region #region
/// <summary> /// <summary>
/// 轮询等待三轴绝对位置均回到零点(容差 ±10),超时则抛出异常 /// 轮询等待三轴绝对位置均回到机械零点(容差 ±10),超时则抛出异常
/// </summary> /// </summary>
/// <param name="超时秒数">最大等待时间(秒)</param> /// <param name="超时秒数">最大等待时间(秒)</param>
/// <param name="取消令牌">取消令牌</param> /// <param name="取消令牌">取消令牌</param>
[Browsable(false)]
public async Task (int , CancellationToken = default) public async Task (int , CancellationToken = default)
{ {
var deadline = DateTime.Now.AddSeconds(); var deadline = DateTime.Now.AddSeconds();
@@ -597,13 +705,43 @@ namespace DeviceCommand.Devices
.ThrowIfCancellationRequested(); .ThrowIfCancellationRequested();
var pos = await (); var pos = await ();
if (Math.Abs(pos.Axis1) <= 10 && Math.Abs(pos.Axis2) <= 10 && Math.Abs(pos.Axis3) <= 10) if (Math.Abs(pos.Axis1) <= 10
&& Math.Abs(pos.Axis6) <= 10
&& Math.Abs(pos.Axis2) <= 10)
return; return;
await Task.Delay(100, ); await Task.Delay(100, );
} }
throw new TimeoutException($"等待 {超时秒数} 秒后,三轴仍未到零点"); throw new TimeoutException($"等待 {超时秒数} 秒后,三轴仍未到达机械零点");
}
/// <summary>
/// 轮询等待三轴绝对位置到达指定的待定位置(容差 ±10),超时则抛出异常
/// </summary>
/// <param name="xTarget">X轴目标位置</param>
/// <param name="yTarget">Y轴目标位置</param>
/// <param name="zTarget">Z轴目标位置</param>
/// <param name="超时秒数">最大等待时间(秒)</param>
/// <param name="取消令牌">取消令牌</param>
private async Task (int xTarget, int yTarget, int zTarget, int , CancellationToken = default)
{
var deadline = DateTime.Now.AddSeconds();
while (DateTime.Now < deadline)
{
.ThrowIfCancellationRequested();
var pos = await ();
if (Math.Abs(pos.Axis1 - xTarget) <= 10
&& Math.Abs(pos.Axis6 - yTarget) <= 10
&& Math.Abs(pos.Axis2 - zTarget) <= 10)
return;
await Task.Delay(100, );
}
throw new TimeoutException($"等待 {超时秒数} 秒后,三轴仍未到达待定位置 (X:{xTarget}, Y:{yTarget}, Z:{zTarget})");
} }
/// <summary> /// <summary>
@@ -625,8 +763,8 @@ namespace DeviceCommand.Devices
var pos = await (); var pos = await ();
if (Math.Abs(pos.Axis1 - targetX) <= 10 if (Math.Abs(pos.Axis1 - targetX) <= 10
&& Math.Abs(pos.Axis2 - targetY) <= 10 && Math.Abs(pos.Axis6 - targetY) <= 10
&& Math.Abs(pos.Axis3 - targetZ) <= 10) && Math.Abs(pos.Axis2 - targetZ) <= 10)
return; return;
await Task.Delay(100, ); await Task.Delay(100, );
+12
View File
@@ -336,6 +336,18 @@ namespace DeviceCommand.Devices
#region 3.6. #region 3.6.
/// <summary>
/// 退出远程控制 (MEASure:EVENT?)
/// </summary>
/// <param name="是否开启">true 开启远程;false关闭远程</param>
/// <param name="ct">取消令牌</param>
public virtual async Task (bool , CancellationToken ct = default)
{
int val = ? 0 : 1;
await SendAsync($"SYSTem:EXIT:REMOte {val}{ScpiDelimiter}", ct);
}
/// <summary> /// <summary>
/// 读取负载报警/事件寄存器信息 (MEASure:EVENT?) /// 读取负载报警/事件寄存器信息 (MEASure:EVENT?)
/// </summary> /// </summary>
+72 -4
View File
@@ -688,6 +688,7 @@ namespace ZLGUSBCANFD
/// <summary> /// <summary>
/// 发送自定义报文(原始字节,帧 ID 支持 string 兼容模式)。 /// 发送自定义报文(原始字节,帧 ID 支持 string 兼容模式)。
/// 循环发送间隔毫秒 = 0 时只发送一次;>0 时按指定间隔循环发送,可调用 停止循环发送 停止。
/// </summary> /// </summary>
/// <param name="通道号">CAN/CANFD 通道索引</param> /// <param name="通道号">CAN/CANFD 通道索引</param>
/// <param name="帧IDStr">帧 ID(可以是 "26"、"0x1A"、"1A");扩展帧会自动置位 0x80000000</param> /// <param name="帧IDStr">帧 ID(可以是 "26"、"0x1A"、"1A");扩展帧会自动置位 0x80000000</param>
@@ -696,6 +697,7 @@ namespace ZLGUSBCANFD
/// <param name="是否是扩展帧">是否为 29 位扩展帧</param> /// <param name="是否是扩展帧">是否为 29 位扩展帧</param>
/// <param name="是否是CANFD">true = CANFDfalse = 经典 CAN</param> /// <param name="是否是CANFD">true = CANFDfalse = 经典 CAN</param>
/// <param name="开启波特率加速BRS">CANFD 是否开启波特率加速</param> /// <param name="开启波特率加速BRS">CANFD 是否开启波特率加速</param>
/// <param name="循环发送间隔毫秒">0 = 单次发送;>0 = 周期循环发送(毫秒)</param>
public virtual bool ( public virtual bool (
uint , uint ,
string IDStr, string IDStr,
@@ -703,7 +705,8 @@ namespace ZLGUSBCANFD
byte dlcLength, byte dlcLength,
bool = false, bool = false,
bool CANFD = true, bool CANFD = true,
bool BRS = true) bool BRS = true,
int = 0)
{ {
if (!TryParseFrameId(IDStr, out uint ID)) if (!TryParseFrameId(IDStr, out uint ID))
{ {
@@ -713,23 +716,88 @@ namespace ZLGUSBCANFD
if ( >= _maxChannels || _channelHandles[] == IntPtr.Zero) return false; if ( >= _maxChannels || _channelHandles[] == IntPtr.Zero) return false;
if ( == null) return false; if ( == null) return false;
if ( < 0) throw new ArgumentOutOfRangeException(nameof(), "循环发送间隔必须大于或等于 0。");
// 扩展帧需要把最高位标志位置起来 // 扩展帧需要把最高位标志位置起来
uint canId = ID & 0x7FFFFFFF; uint canId = ID & 0x7FFFFFFF;
if () canId |= 0x80000000; if () canId |= 0x80000000;
// 单次发送
if ( == 0)
{
return (, canId, , dlcLength, , CANFD, BRS);
}
// 循环发送:先停止同通道同帧 ID 的旧循环,再启动新循环
(, ID);
// 拷贝原始数据,避免外部修改影响循环发送
byte[] dataCopy = new byte[.Length];
Array.Copy(, dataCopy, .Length);
var cts = new CancellationTokenSource();
var sendTask = Task.Run(async () =>
{
while (!cts.Token.IsCancellationRequested)
{
if (_isClosing) break;
try
{
lock (_channelLocks[])
{
if (_channelHandles[] == IntPtr.Zero || _isClosing) break;
(, canId, dataCopy, dlcLength, , CANFD, BRS);
}
await Task.Delay(, cts.Token);
}
catch (OperationCanceledException)
{
break;
}
catch (Exception ex)
{
Logger.LoggerHelper.Error($"[ZLGCANFD] 循环发送自定义报文失败: {ex.Message}");
break;
}
}
if (_cyclicSenders.TryGetValue((, ID), out var current) && ReferenceEquals(current.Cts, cts))
{
_cyclicSenders.TryRemove((, ID), out _);
}
cts.Dispose();
}, cts.Token);
_cyclicSenders[(, ID)] = (cts, sendTask);
return true;
}
/// <summary>
/// 发送自定义报文的单次执行(内部方法,无循环逻辑)。
/// </summary>
[Browsable(false)]
private bool (
uint ,
uint canId,
byte[] ,
byte dlcLength,
bool ,
bool CANFD,
bool BRS)
{
lock (_channelLocks[]) lock (_channelLocks[])
{ {
if (CANFD) if (CANFD)
{ {
// CANFD 有效 DLC 校验
byte validFdDlc = CANFD的Dlc(dlcLength); byte validFdDlc = CANFD的Dlc(dlcLength);
ZLGCAN.canfd_frame fdFrame = new ZLGCAN.canfd_frame ZLGCAN.canfd_frame fdFrame = new ZLGCAN.canfd_frame
{ {
can_id = canId, can_id = canId,
len = validFdDlc, len = validFdDlc,
flags = (byte)(( ? 0x01 : 0x00) | (BRS ? 0x02 : 0x00) | 0x20), // 0x20 本地回显 flags = (byte)(( ? 0x01 : 0x00) | (BRS ? 0x02 : 0x00) | 0x20),
data = new byte[64] data = new byte[64]
}; };
Array.Copy(, 0, fdFrame.data, 0, Math.Min(.Length, 64)); Array.Copy(, 0, fdFrame.data, 0, Math.Min(.Length, 64));
@@ -749,7 +817,7 @@ namespace ZLGUSBCANFD
{ {
can_id = canId, can_id = canId,
can_dlc = dlcLength > 8 ? (byte)8 : dlcLength, can_dlc = dlcLength > 8 ? (byte)8 : dlcLength,
__pad = 0x20, // 发送回显 __pad = 0x20,
data = new byte[8] data = new byte[8]
}; };
Array.Copy(, 0, standardFrame.data, 0, Math.Min(.Length, 8)); Array.Copy(, 0, standardFrame.data, 0, Math.Min(.Length, 8));