台架程序tp化
This commit is contained in:
@@ -1,7 +1,5 @@
|
||||
using System.Reflection;
|
||||
using BenchMovementModule.Views;
|
||||
using BenchMovementModule.Views.Dialogs;
|
||||
using BenchMovementModule.ViewModels.Dialogs;
|
||||
|
||||
namespace BenchMovementModule
|
||||
{
|
||||
@@ -16,9 +14,6 @@ namespace BenchMovementModule
|
||||
public void RegisterTypes(IContainerRegistry containerRegistry)
|
||||
{
|
||||
containerRegistry.RegisterForNavigation<BenchMovementView>("BenchMovementView");
|
||||
|
||||
// 台架 TCP 连接配置弹窗
|
||||
containerRegistry.RegisterDialog<GantryConfigDialogView, GantryConfigDialogViewModel>("GantryConfigDialog");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,548 +0,0 @@
|
||||
using DeviceCommand.Base;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Threading;
|
||||
|
||||
namespace BenchMovementModule.HardwareDrive
|
||||
{
|
||||
public abstract class GantryControlBase
|
||||
{
|
||||
protected readonly IModbusDevice _device;
|
||||
protected readonly byte _slaveAddress;
|
||||
|
||||
// =================================================================
|
||||
// ------------------ Coil / Bit 状态与控制 (位元件) ------------------
|
||||
// =================================================================
|
||||
protected const ushort ADDR_M_ESTOP = 20496; // 0x5010: 紧停
|
||||
protected const ushort ADDR_M_X_STOP = 41; // 0x29: X轴停止
|
||||
protected const ushort ADDR_M_Y_STOP = 42; // 0x2A: Y轴停止
|
||||
protected const ushort ADDR_M_Z_STOP = 43; // 0x2B: Z轴停止
|
||||
|
||||
protected const ushort ADDR_M_X_START = 30; // 0x1E: X轴指定位置启动
|
||||
protected const ushort ADDR_M_Y_START = 31; // 0x1F: Y轴指定位置启动
|
||||
protected const ushort ADDR_M_Z_START = 35; // 0x23: Z轴指定位置启动
|
||||
|
||||
protected const ushort ADDR_M_HOME_TRIGGER = 100; // 0x64: 回原点触发
|
||||
|
||||
// =================================================================
|
||||
// ------------------ 轴使能控制 (HM 寄存器) ------------------
|
||||
// =================================================================
|
||||
protected const ushort ADDR_HM_AXIS1_ENABLE = 49408; // 0xC100: 轴1使能 (X轴)
|
||||
protected const ushort ADDR_HM_AXIS2_ENABLE = 49409; // 0xC101: 轴2使能 (Y轴)
|
||||
protected const ushort ADDR_HM_AXIS3_ENABLE = 49410; // 0xC102: 轴3使能 (Z轴)
|
||||
protected const ushort ADDR_HM_AXIS4_ENABLE = 49411; // 0xC103: 轴4使能
|
||||
protected const ushort ADDR_HM_AXIS5_ENABLE = 49412; // 0xC104: 轴5使能
|
||||
protected const ushort ADDR_HM_AXIS6_ENABLE = 49413; // 0xC105: 轴6使能
|
||||
|
||||
// =================================================================
|
||||
// ------------------ 速度与位置参数 (HD 32位数据寄存器) --------------
|
||||
// =================================================================
|
||||
protected const ushort ADDR_HD_HOME_POS_SPEED = 41288; // 0xA148: 原点定位速度 (32位)
|
||||
|
||||
// 轴原点位置
|
||||
protected const ushort ADDR_HD_X_HOME_POS = 41198; // 0xA0EE: X轴原点位 (32位)
|
||||
protected const ushort ADDR_HD_Z_HOME_POS = 41208; // 0xA0F8: Z轴原点位 (32位)
|
||||
protected const ushort ADDR_HD_Y_HOME_POS = 41218; // 0xA102: Y轴原点位 (32位)
|
||||
|
||||
// 轴运行目标位置
|
||||
protected const ushort ADDR_HD_Y_TARGET = 41228; // 0xA10C: Y轴指定位置 (32位)
|
||||
protected const ushort ADDR_HD_Z_TARGET = 41238; // 0xA116: Z轴指定位置 (32位)
|
||||
protected const ushort ADDR_HD_X_TARGET = 41248; // 0xA120: X轴指定位置 (32位)
|
||||
|
||||
// 轴运行速度
|
||||
protected const ushort ADDR_HD_X_SPEED = 41318; // 0xA166: X轴速度 (32位)
|
||||
protected const ushort ADDR_HD_Z_SPEED = 41328; // 0xA170: Z轴速度 (32位)
|
||||
protected const ushort ADDR_HD_Y_SPEED = 41338; // 0xA17A: Y轴速度 (32位)
|
||||
|
||||
// =================================================================
|
||||
// ------------------ 轴绝对位置(只读,相对原点)------------------
|
||||
// =================================================================
|
||||
protected const ushort ADDR_ABS_AXIS1_POS = 47232; // 轴1绝对位置 (32位)
|
||||
protected const ushort ADDR_ABS_AXIS2_POS = 47236; // 轴2绝对位置 (32位)
|
||||
protected const ushort ADDR_ABS_AXIS3_POS = 47240; // 轴3绝对位置 (32位)
|
||||
protected const ushort ADDR_ABS_AXIS4_POS = 47244; // 轴4绝对位置 (32位)
|
||||
protected const ushort ADDR_ABS_AXIS5_POS = 47248; // 轴5绝对位置 (32位)
|
||||
protected const ushort ADDR_ABS_AXIS6_POS = 47252; // 轴6绝对位置 (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<bool> ConnectAsync(CancellationToken ct = default)
|
||||
{
|
||||
return await _device.ConnectAsync(ct);
|
||||
}
|
||||
|
||||
public void Close()
|
||||
{
|
||||
_device.Close();
|
||||
}
|
||||
|
||||
// =================================================================
|
||||
// ------------------ 核心控制逻辑与参数读写 ------------------------
|
||||
// =================================================================
|
||||
|
||||
/// <summary>
|
||||
/// 轴使能控制
|
||||
/// </summary>
|
||||
/// <param name="axis">轴编号 (1 - 6)</param>
|
||||
public async Task SetAxisEnableAsync(int axis, bool enable, CancellationToken ct = default)
|
||||
{
|
||||
ushort address = axis switch
|
||||
{
|
||||
1 => ADDR_HM_AXIS1_ENABLE,
|
||||
2 => ADDR_HM_AXIS2_ENABLE,
|
||||
3 => ADDR_HM_AXIS3_ENABLE,
|
||||
4 => ADDR_HM_AXIS4_ENABLE,
|
||||
5 => ADDR_HM_AXIS5_ENABLE,
|
||||
6 => ADDR_HM_AXIS6_ENABLE,
|
||||
_ => throw new ArgumentException("轴通道错误,仅支持 1 - 6", nameof(axis))
|
||||
};
|
||||
|
||||
await _device.WriteSingleCoilAsync(_slaveAddress, address, enable, ct);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 读取指定轴的使能状态
|
||||
/// </summary>
|
||||
/// <param name="axis">1: X轴, 2: Y轴, 3: Z轴</param>
|
||||
public async Task<bool> GetAxisEnableAsync(int axis, CancellationToken ct = default)
|
||||
{
|
||||
ushort address = axis switch
|
||||
{
|
||||
1 => ADDR_HM_AXIS1_ENABLE,
|
||||
2 => ADDR_HM_AXIS2_ENABLE,
|
||||
3 => ADDR_HM_AXIS3_ENABLE,
|
||||
_ => throw new ArgumentException("轴通道错误,仅支持 1(X), 2(Y), 3(Z)", nameof(axis))
|
||||
};
|
||||
|
||||
bool[] coils = await _device.ReadCoilsAsync(_slaveAddress, address, 1, ct);
|
||||
return coils != null && coils.Length > 0 && coils[0];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 同时设置 X/Y/Z 轴的原点位置(带各自独立的范围检查)
|
||||
/// </summary>
|
||||
/// <param name="xPos">X轴原点位置 (范围: 0 ~ 50000)</param>
|
||||
/// <param name="yPos">Y轴原点位置 (范围: 0 ~ 50000)</param>
|
||||
/// <param name="zPos">Z轴原点位置 (范围: 0 ~ 20000)</param>
|
||||
public async Task SetHomePositionAsync(int xPos, int yPos, int zPos, CancellationToken ct = default)
|
||||
{
|
||||
if (xPos < 0 || xPos > 50000)
|
||||
throw new ArgumentOutOfRangeException(nameof(xPos), $"X轴原点设置值 [{xPos}] 超出合法范围 (0 - 50000)");
|
||||
|
||||
if (yPos < 0 || yPos > 50000)
|
||||
throw new ArgumentOutOfRangeException(nameof(yPos), $"Y轴原点设置值 [{yPos}] 超出合法范围 (0 - 50000)");
|
||||
|
||||
if (zPos < 0 || zPos > 20000)
|
||||
throw new ArgumentOutOfRangeException(nameof(zPos), $"Z轴原点设置值 [{zPos}] 超出合法范围 (0 - 20000)");
|
||||
|
||||
await _device.WriteMultipleRegistersAsync(_slaveAddress, ADDR_HD_X_HOME_POS, Int32ToUshorts(xPos), ct);
|
||||
await _device.WriteMultipleRegistersAsync(_slaveAddress, ADDR_HD_Y_HOME_POS, Int32ToUshorts(yPos), ct);
|
||||
await _device.WriteMultipleRegistersAsync(_slaveAddress, ADDR_HD_Z_HOME_POS, Int32ToUshorts(zPos), ct);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 批量查询当前 X、Y、Z 三轴设定的原点位置值
|
||||
/// </summary>
|
||||
/// <returns>返回包含三轴原点位置的元组 (X, Y, Z)</returns>
|
||||
public async Task<(int X, int Y, int Z)> GetHomePositionAsync(CancellationToken ct = default)
|
||||
{
|
||||
// 41218(Y) - 41198(X) = 20,加Y轴自身的 2 个寄存器,共批量读取 22 个连续寄存器
|
||||
ushort[] registers = await _device.ReadHoldingRegistersAsync(_slaveAddress, ADDR_HD_X_HOME_POS, 22, ct);
|
||||
|
||||
if (registers == null || registers.Length < 22)
|
||||
throw new InvalidOperationException("从 PLC 读取三轴原点位置失败,返回数据长度不足。");
|
||||
|
||||
int xHomePos = UshortsToInt32(new ushort[] { registers[0], registers[1] });
|
||||
int zHomePos = UshortsToInt32(new ushort[] { registers[10], registers[11] });
|
||||
int yHomePos = UshortsToInt32(new ushort[] { registers[20], registers[21] });
|
||||
|
||||
return (xHomePos, yHomePos, zHomePos);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置回原点定位速度(带 0 - 100000 范围检查)
|
||||
/// </summary>
|
||||
public async Task SetHomePositionSpeedAsync(int speed, CancellationToken ct = default)
|
||||
{
|
||||
if (speed < 0 || speed > 100000)
|
||||
throw new ArgumentOutOfRangeException(nameof(speed), $"回原点定位速度值 [{speed}] 超出合法范围 (0 - 100000)");
|
||||
|
||||
await _device.WriteMultipleRegistersAsync(_slaveAddress, ADDR_HD_HOME_POS_SPEED, Int32ToUshorts(speed), ct);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查询当前设定的回原点定位速度
|
||||
/// </summary>
|
||||
public async Task<int> GetHomePositionSpeedAsync(CancellationToken ct = default)
|
||||
{
|
||||
ushort[] registers = await _device.ReadHoldingRegistersAsync(_slaveAddress, ADDR_HD_HOME_POS_SPEED, 2, ct);
|
||||
|
||||
if (registers == null || registers.Length < 2)
|
||||
throw new InvalidOperationException("从 PLC 读取回原点定位速度失败,返回数据长度不足。");
|
||||
|
||||
return UshortsToInt32(registers);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置指定轴的运行速度(带 0 - 100000 范围检查)
|
||||
/// </summary>
|
||||
/// <param name="axis">1: X轴, 2: Y轴, 3: Z轴</param>
|
||||
public async Task SetAxisSpeedAsync(int axis, int speed, CancellationToken ct = default)
|
||||
{
|
||||
if (speed < 0 || speed > 100000)
|
||||
throw new ArgumentOutOfRangeException(nameof(speed), $"轴速度设置值 [{speed}] 超出合法范围 (0 - 100000)");
|
||||
|
||||
ushort address = axis switch
|
||||
{
|
||||
1 => ADDR_HD_X_SPEED,
|
||||
2 => ADDR_HD_Y_SPEED,
|
||||
3 => ADDR_HD_Z_SPEED,
|
||||
_ => throw new ArgumentException("轴通道错误,仅支持 1(X), 2(Y), 3(Z)", nameof(axis))
|
||||
};
|
||||
|
||||
await _device.WriteMultipleRegistersAsync(_slaveAddress, address, Int32ToUshorts(speed), ct);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 读取指定轴当前设定的运行速度
|
||||
/// </summary>
|
||||
public async Task<int> GetAxisSpeedAsync(int axis, CancellationToken ct = default)
|
||||
{
|
||||
ushort address = axis switch
|
||||
{
|
||||
1 => ADDR_HD_X_SPEED,
|
||||
2 => ADDR_HD_Y_SPEED,
|
||||
3 => ADDR_HD_Z_SPEED,
|
||||
_ => throw new ArgumentException("轴通道错误,仅支持 1(X), 2(Y), 3(Z)", nameof(axis))
|
||||
};
|
||||
|
||||
ushort[] registers = await _device.ReadHoldingRegistersAsync(_slaveAddress, address, 2, ct);
|
||||
if (registers == null || registers.Length < 2)
|
||||
throw new InvalidOperationException($"从 PLC 读取轴 {axis} 速度失败。");
|
||||
|
||||
return UshortsToInt32(registers);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 写入指定轴的目标位置(动态安全校验:目标位置不能超过 [轴最大物理极限 - 当前轴原点设置值])
|
||||
/// </summary>
|
||||
public async Task SetAxisTargetPositionAsync(int axis, int targetPos, CancellationToken ct = default)
|
||||
{
|
||||
var (xHome, yHome, zHome) = await GetHomePositionAsync(ct);
|
||||
|
||||
ushort targetAddress;
|
||||
int maxLimit;
|
||||
int currentHomePos;
|
||||
|
||||
switch (axis)
|
||||
{
|
||||
case 1:
|
||||
targetAddress = ADDR_HD_X_TARGET;
|
||||
maxLimit = 50000;
|
||||
currentHomePos = xHome;
|
||||
break;
|
||||
case 2:
|
||||
targetAddress = ADDR_HD_Y_TARGET;
|
||||
maxLimit = 50000;
|
||||
currentHomePos = yHome;
|
||||
break;
|
||||
case 3:
|
||||
targetAddress = ADDR_HD_Z_TARGET;
|
||||
maxLimit = 20000;
|
||||
currentHomePos = zHome;
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentException("轴通道错误,仅支持 1(X), 2(Y), 3(Z)", nameof(axis));
|
||||
}
|
||||
|
||||
int allowedMaxLimit = maxLimit - currentHomePos;
|
||||
|
||||
if (targetPos < 0 || targetPos > allowedMaxLimit)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(
|
||||
nameof(targetPos),
|
||||
$"轴 {axis} 目标位置 [{targetPos}] 不合法!当前原点为 [{currentHomePos}],允许的有效写入范围为: 0 - {allowedMaxLimit}"
|
||||
);
|
||||
}
|
||||
|
||||
await _device.WriteMultipleRegistersAsync(_slaveAddress, targetAddress, Int32ToUshorts(targetPos), ct);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 读取指定轴当前设定的目标指定位置
|
||||
/// </summary>
|
||||
public async Task<int> GetAxisTargetPositionAsync(int axis, CancellationToken ct = default)
|
||||
{
|
||||
ushort address = axis switch
|
||||
{
|
||||
1 => ADDR_HD_X_TARGET,
|
||||
2 => ADDR_HD_Y_TARGET,
|
||||
3 => ADDR_HD_Z_TARGET,
|
||||
_ => throw new ArgumentException("轴通道错误,仅支持 1(X), 2(Y), 3(Z)", nameof(axis))
|
||||
};
|
||||
|
||||
ushort[] registers = await _device.ReadHoldingRegistersAsync(_slaveAddress, address, 2, ct);
|
||||
|
||||
if (registers == null || registers.Length < 2)
|
||||
throw new InvalidOperationException($"从 PLC 读取轴 {axis} 目标位置失败。");
|
||||
|
||||
return UshortsToInt32(registers);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置系统紧停锁定状态 (true: 锁定激活, false: 解除锁定)
|
||||
/// </summary>
|
||||
public async Task SetEStopStateAsync(bool state, CancellationToken ct = default)
|
||||
{
|
||||
await _device.WriteSingleCoilAsync(_slaveAddress, ADDR_M_ESTOP, state, ct);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置指定轴的停止控制状态 (true: 持续输出停止信号, false: 释放停止信号)
|
||||
/// </summary>
|
||||
public async Task SetStopStateAsync(int axis, bool state, CancellationToken ct = default)
|
||||
{
|
||||
ushort address = axis switch
|
||||
{
|
||||
1 => ADDR_M_X_STOP,
|
||||
2 => ADDR_M_Y_STOP,
|
||||
3 => ADDR_M_Z_STOP,
|
||||
_ => throw new ArgumentException("轴通道错误,仅支持 1(X), 2(Y), 3(Z)", nameof(axis))
|
||||
};
|
||||
|
||||
await _device.WriteSingleCoilAsync(_slaveAddress, address, state, ct);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 全局回零(安全校验版:先检查回原点速度是否为 0,为0则拒绝触发)
|
||||
/// </summary>
|
||||
public async Task HomeAsync(CancellationToken ct = default)
|
||||
{
|
||||
int currentSpeed = await GetHomePositionSpeedAsync(ct);
|
||||
|
||||
if (currentSpeed == 0)
|
||||
throw new InvalidOperationException("无法触发回原点!当前回原点定位速度为 0,请先设置合法的回零速度。");
|
||||
|
||||
await TriggerCoilAsync(ADDR_M_HOME_TRIGGER, ct);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 单轴指定位置启动(含速度判定、使能自动补齐、动作目标位置独立范围校验)
|
||||
/// </summary>
|
||||
/// <param name="axis">1: X轴, 2: Y轴, 3: Z轴</param>
|
||||
public async Task StartSingleAxisAsync(int axis, CancellationToken ct = default)
|
||||
{
|
||||
ushort startAddress;
|
||||
int maxLimit;
|
||||
int currentHomePos;
|
||||
|
||||
// 1. 获取基础参数与校验边界
|
||||
var (xHome, yHome, zHome) = await GetHomePositionAsync(ct);
|
||||
switch (axis)
|
||||
{
|
||||
case 1:
|
||||
startAddress = ADDR_M_X_START;
|
||||
maxLimit = 50000;
|
||||
currentHomePos = xHome;
|
||||
break;
|
||||
case 2:
|
||||
startAddress = ADDR_M_Y_START;
|
||||
maxLimit = 50000;
|
||||
currentHomePos = yHome;
|
||||
break;
|
||||
case 3:
|
||||
startAddress = ADDR_M_Z_START;
|
||||
maxLimit = 20000;
|
||||
currentHomePos = zHome;
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentException("轴通道错误,仅支持 1(X), 2(Y), 3(Z)", nameof(axis));
|
||||
}
|
||||
|
||||
// 2. 检查动作轴的速度
|
||||
int currentSpeed = await GetAxisSpeedAsync(axis, ct);
|
||||
if (currentSpeed == 0)
|
||||
throw new InvalidOperationException($"轴 {axis} 无法启动!当前设定速度为 0。");
|
||||
|
||||
// 3. 检查动作轴使能,若为 false 则自动补写 true 激活驱动器
|
||||
bool isEnabled = await GetAxisEnableAsync(axis, ct);
|
||||
if (!isEnabled)
|
||||
{
|
||||
await SetAxisEnableAsync(axis, true, ct);
|
||||
await Task.Delay(100, ct); // 等待硬件继电器响应与伺服驱动就绪
|
||||
}
|
||||
|
||||
// 4. 精准独立校验当前需要动作轴的目标定位是否越界
|
||||
int currentTarget = await GetAxisTargetPositionAsync(axis, ct);
|
||||
int allowedMaxLimit = maxLimit - currentHomePos;
|
||||
if (currentTarget < 0 || currentTarget > allowedMaxLimit)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException($"轴 {axis} 无法启动!当前指定目标位置 [{currentTarget}] 超过了允许的最大安全极限值 [{allowedMaxLimit}]。");
|
||||
}
|
||||
|
||||
// 5. 校验完成,边缘触发点动启动
|
||||
await TriggerCoilAsync(startAddress, ct);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 三轴同时启动(全开:严格校验全部运行轴速度、批量检测与补正1~6号轴使能、联动校验三轴目标范围极限)
|
||||
/// </summary>
|
||||
public async Task StartAllAxesAsync(CancellationToken ct = default)
|
||||
{
|
||||
// 1. 一次性读取三轴速度,严禁任何一轴速度为 0
|
||||
int xSpeed = await GetAxisSpeedAsync(1, ct);
|
||||
int ySpeed = await GetAxisSpeedAsync(2, ct);
|
||||
int zSpeed = await GetAxisSpeedAsync(3, ct);
|
||||
|
||||
if (xSpeed == 0 || ySpeed == 0 || zSpeed == 0)
|
||||
throw new InvalidOperationException($"无法全开启动!存在运行速度为 0 的轴。当前速度 -> X:{xSpeed}, Y:{ySpeed}, Z:{zSpeed}");
|
||||
|
||||
// 2. 批量读取 1 ~ 6 号轴连续的使能状态线圈,极大地优化网络性能
|
||||
bool[] enableStates = await _device.ReadCoilsAsync(_slaveAddress, ADDR_HM_AXIS1_ENABLE, 6, ct);
|
||||
if (enableStates == null || enableStates.Length < 6)
|
||||
throw new InvalidOperationException("从 PLC 读取 1~6 号轴使能状态失败。");
|
||||
|
||||
bool hasModifiedEnable = false;
|
||||
ushort[] enableAddresses = { ADDR_HM_AXIS1_ENABLE, ADDR_HM_AXIS2_ENABLE, ADDR_HM_AXIS3_ENABLE, ADDR_HM_AXIS4_ENABLE, ADDR_HM_AXIS5_ENABLE, ADDR_HM_AXIS6_ENABLE };
|
||||
|
||||
for (int i = 0; i < 6; i++)
|
||||
{
|
||||
if (!enableStates[i])
|
||||
{
|
||||
await _device.WriteSingleCoilAsync(_slaveAddress, enableAddresses[i], true, ct);
|
||||
hasModifiedEnable = true;
|
||||
}
|
||||
}
|
||||
|
||||
// 如果执行过使能补齐补齐写入,在循环外进行统一延时,保证PLC时序安全
|
||||
if (hasModifiedEnable)
|
||||
await Task.Delay(100, ct);
|
||||
|
||||
// 3. 获取三轴原点及当前设定的目标指定位置
|
||||
var (xHome, yHome, zHome) = await GetHomePositionAsync(ct);
|
||||
|
||||
int xTarget = await GetAxisTargetPositionAsync(1, ct);
|
||||
int yTarget = await GetAxisTargetPositionAsync(2, ct);
|
||||
int zTarget = await GetAxisTargetPositionAsync(3, ct);
|
||||
|
||||
// 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 _device.WriteSingleCoilAsync(_slaveAddress, ADDR_M_X_START, true, ct);
|
||||
await _device.WriteSingleCoilAsync(_slaveAddress, ADDR_M_Y_START, true, ct);
|
||||
await _device.WriteSingleCoilAsync(_slaveAddress, ADDR_M_Z_START, true, ct);
|
||||
|
||||
await Task.Delay(100, ct); // 保持高电平状态以适配 PLC 扫描周期
|
||||
|
||||
await _device.WriteSingleCoilAsync(_slaveAddress, ADDR_M_X_START, false, ct);
|
||||
await _device.WriteSingleCoilAsync(_slaveAddress, ADDR_M_Y_START, false, ct);
|
||||
await _device.WriteSingleCoilAsync(_slaveAddress, ADDR_M_Z_START, false, ct);
|
||||
}
|
||||
|
||||
#region 内部高低字工具转化方法
|
||||
|
||||
/// <summary>
|
||||
/// 触发点动脉冲信号(置 1 后,等待 100ms 自动置 0 释放)
|
||||
/// </summary>
|
||||
protected async Task TriggerCoilAsync(ushort address, CancellationToken ct = default)
|
||||
{
|
||||
await _device.WriteSingleCoilAsync(_slaveAddress, address, true, ct);
|
||||
await Task.Delay(100, ct);
|
||||
await _device.WriteSingleCoilAsync(_slaveAddress, address, false, ct);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将 32 位整型数据转换为 Modbus 的 2 个 16 位无符号整数(低字在前 CDAB 格式)
|
||||
/// </summary>
|
||||
protected ushort[] Int32ToUshorts(int value)
|
||||
{
|
||||
ushort lowWord = (ushort)(value & 0xFFFF);
|
||||
ushort highWord = (ushort)((value >> 16) & 0xFFFF);
|
||||
return new ushort[] { lowWord, highWord };
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将 Modbus 的 2 个 16 位无符号整数转换为 32 位整型(低字在前 CDAB 格式)
|
||||
/// </summary>
|
||||
protected int UshortsToInt32(ushort[] registers)
|
||||
{
|
||||
uint lowWord = registers[0];
|
||||
uint highWord = registers[1];
|
||||
return (int)((highWord << 16) | lowWord);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 轴绝对位置读取(只读)
|
||||
|
||||
/// <summary>
|
||||
/// 读取指定轴的绝对位置(相对原点的实时位置,只读)
|
||||
/// </summary>
|
||||
/// <param name="axis">轴编号 (1-6)</param>
|
||||
public async Task<int> GetAbsolutePositionAsync(int axis, CancellationToken ct = default)
|
||||
{
|
||||
ushort address = axis switch
|
||||
{
|
||||
1 => ADDR_ABS_AXIS1_POS,
|
||||
2 => ADDR_ABS_AXIS2_POS,
|
||||
3 => ADDR_ABS_AXIS3_POS,
|
||||
4 => ADDR_ABS_AXIS4_POS,
|
||||
5 => ADDR_ABS_AXIS5_POS,
|
||||
6 => ADDR_ABS_AXIS6_POS,
|
||||
_ => throw new ArgumentException("轴编号错误,仅支持 1-6", nameof(axis))
|
||||
};
|
||||
|
||||
ushort[] registers = await _device.ReadHoldingRegistersAsync(_slaveAddress, address, 2, ct);
|
||||
if (registers == null || registers.Length < 2)
|
||||
throw new InvalidOperationException($"读取轴 {axis} 绝对位置失败。");
|
||||
|
||||
return UshortsToInt32(registers);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 批量读取 1-6 轴的绝对位置(一次性读取 22 个寄存器,高效)
|
||||
/// </summary>
|
||||
/// <returns>返回 6 轴绝对位置元组</returns>
|
||||
public async Task<(int Axis1, int Axis2, int Axis3, int Axis4, int Axis5, int Axis6)> GetAllAbsolutePositionsAsync(CancellationToken ct = default)
|
||||
{
|
||||
// 从 47232 开始连续读取 22 个寄存器(覆盖 47232-47253)
|
||||
ushort[] registers = await _device.ReadHoldingRegistersAsync(_slaveAddress, ADDR_ABS_AXIS1_POS, 22, ct);
|
||||
if (registers == null || registers.Length < 22)
|
||||
throw new InvalidOperationException("批量读取 6 轴绝对位置失败,返回数据长度不足。");
|
||||
|
||||
// 每轴间隔 4 个寄存器(2个数据 + 2个间隙),提取各轴数据
|
||||
int axis1 = UshortsToInt32(new ushort[] { registers[0], registers[1] });
|
||||
int axis2 = UshortsToInt32(new ushort[] { registers[4], registers[5] });
|
||||
int axis3 = UshortsToInt32(new ushort[] { registers[8], registers[9] });
|
||||
int axis4 = UshortsToInt32(new ushort[] { registers[12], registers[13] });
|
||||
int axis5 = UshortsToInt32(new ushort[] { registers[16], registers[17] });
|
||||
int axis6 = UshortsToInt32(new ushort[] { registers[20], registers[21] });
|
||||
|
||||
return (axis1, axis2, axis3, axis4, axis5, axis6);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
using DeviceCommand.Base;
|
||||
using Model.Models;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace BenchMovementModule.HardwareDrive
|
||||
{
|
||||
public class GantryControlTcp : GantryControlBase
|
||||
{
|
||||
// 暴露出底层的 ModbusTcp 实例以便修改网络配置
|
||||
public ModbusTcp TcpDevice => (ModbusTcp)_device;
|
||||
|
||||
public GantryControlTcp(TcpConfig config, byte slaveAddress = 1)
|
||||
: base(new ModbusTcp(config), slaveAddress)
|
||||
{
|
||||
}
|
||||
|
||||
public GantryControlTcp(string ipAddress, int port = 502, byte slaveAddress = 1)
|
||||
: base(new ModbusTcp(), slaveAddress)
|
||||
{
|
||||
TcpDevice.ConfigureDevice(ipAddress, port);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,13 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Threading;
|
||||
using BenchMovementModule.HardwareDrive;
|
||||
using DeviceCommand.Devices;
|
||||
using Logger;
|
||||
using Prism.Ioc;
|
||||
using UIShare.GlobalVariable;
|
||||
using UIShare.ViewModelBase;
|
||||
|
||||
namespace BenchMovementModule.ViewModels
|
||||
@@ -15,8 +18,10 @@ namespace BenchMovementModule.ViewModels
|
||||
|
||||
public bool KeepAlive => true;
|
||||
|
||||
// ===== GantryControlTcp 实例 =====
|
||||
// ===== GantryControlTcp 实例(从 DeviceManager 获取)=====
|
||||
private GantryControlTcp? _gantry;
|
||||
private DeviceManager? _deviceManager;
|
||||
private readonly GlobalInfo _globalInfo;
|
||||
|
||||
// ===== 连接状态 =====
|
||||
private bool _isConnected;
|
||||
@@ -116,7 +121,6 @@ namespace BenchMovementModule.ViewModels
|
||||
#endregion
|
||||
|
||||
#region 命令
|
||||
public ICommand OpenConfigCommand { get; }
|
||||
public ICommand ConnectCommand { get; }
|
||||
public ICommand DisconnectCommand { get; }
|
||||
public ICommand MoveXCommand { get; }
|
||||
@@ -140,10 +144,7 @@ namespace BenchMovementModule.ViewModels
|
||||
|
||||
public BenchMovementViewModel(IContainerProvider containerProvider) : base(containerProvider)
|
||||
{
|
||||
// 创建 GantryControlTcp 实例(默认配置,用户可通过弹窗修改)
|
||||
_gantry = new GantryControlTcp("192.168.0.30", 502);
|
||||
|
||||
OpenConfigCommand = new DelegateCommand(OnOpenConfig);
|
||||
_globalInfo = containerProvider.Resolve<GlobalInfo>();
|
||||
ConnectCommand = new DelegateCommand(OnConnect);
|
||||
DisconnectCommand = new DelegateCommand(OnDisconnect);
|
||||
MoveXCommand = new DelegateCommand(OnMoveX);
|
||||
@@ -169,16 +170,6 @@ namespace BenchMovementModule.ViewModels
|
||||
|
||||
#region 命令处理
|
||||
|
||||
private void OnOpenConfig()
|
||||
{
|
||||
var param = new DialogParameters();
|
||||
param.Add("Gantry", _gantry);
|
||||
_dialogService.ShowDialog("GantryConfigDialog", param, result =>
|
||||
{
|
||||
UpdateConnectionStatus();
|
||||
});
|
||||
}
|
||||
|
||||
private async void OnConnect()
|
||||
{
|
||||
if (_gantry == null) return;
|
||||
@@ -470,14 +461,60 @@ namespace BenchMovementModule.ViewModels
|
||||
if (!_isInitiated)
|
||||
{
|
||||
_isInitiated = true;
|
||||
InitGantryFromDeviceManager();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从 DeviceManager.DeviceMap 中查找 GantryControlTcp 实例,
|
||||
/// 优先使用当前打开的作用域,找不到则取任意已注册作用域。
|
||||
/// </summary>
|
||||
private void InitGantryFromDeviceManager()
|
||||
{
|
||||
try
|
||||
{
|
||||
// 优先使用当前打开的作用域
|
||||
IScopedProvider? scope = null;
|
||||
if (!string.IsNullOrEmpty(_globalInfo.CurrentOpeningScope)
|
||||
&& _globalInfo.ScopeDic.TryGetValue(_globalInfo.CurrentOpeningScope, out scope))
|
||||
{
|
||||
// 找到了
|
||||
}
|
||||
else
|
||||
{
|
||||
// 退而求其次,取任意可用作用域
|
||||
scope = _globalInfo.ScopeDic.Values.FirstOrDefault();
|
||||
}
|
||||
|
||||
if (scope == null)
|
||||
{
|
||||
LoggerHelper.Warn("[BenchMovement] 无可用的作用域,无法解析 DeviceManager。");
|
||||
return;
|
||||
}
|
||||
|
||||
_deviceManager = scope.Resolve<DeviceManager>();
|
||||
_gantry = _deviceManager.DeviceMap.Values.OfType<GantryControlTcp>().FirstOrDefault();
|
||||
|
||||
if (_gantry != null)
|
||||
{
|
||||
LoggerHelper.Info($"[BenchMovement] 已从 DeviceManager 获取 GantryControlTcp 实例。");
|
||||
UpdateConnectionStatus();
|
||||
}
|
||||
else
|
||||
{
|
||||
LoggerHelper.Warn("[BenchMovement] DeviceManager 中未找到 GantryControlTcp 设备,请检查设备配置。");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LoggerHelper.Error($"[BenchMovement] 初始化 GantryControlTcp 失败: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_pollTimer.Stop();
|
||||
_gantry?.Close();
|
||||
_gantry?.TcpDevice?.Dispose();
|
||||
// GantryControlTcp 由 DeviceManager 统一管理生命周期,此处仅停止轮询
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -1,202 +0,0 @@
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Windows.Input;
|
||||
using BenchMovementModule.HardwareDrive;
|
||||
using Logger;
|
||||
using UIShare.ViewModelBase;
|
||||
|
||||
namespace BenchMovementModule.ViewModels.Dialogs
|
||||
{
|
||||
public class GantryConfigDialogViewModel : DialogViewModelBase
|
||||
{
|
||||
#region 属性
|
||||
|
||||
private string _title = "台架 TCP 连接配置";
|
||||
public string Title
|
||||
{
|
||||
get => _title;
|
||||
set => SetProperty(ref _title, value);
|
||||
}
|
||||
|
||||
private string _ipAddress = "192.168.0.30";
|
||||
public string IpAddress
|
||||
{
|
||||
get => _ipAddress;
|
||||
set => SetProperty(ref _ipAddress, value);
|
||||
}
|
||||
|
||||
private int _port = 502;
|
||||
public int Port
|
||||
{
|
||||
get => _port;
|
||||
set => SetProperty(ref _port, value);
|
||||
}
|
||||
|
||||
private int _sendTimeout = 3000;
|
||||
public int SendTimeout
|
||||
{
|
||||
get => _sendTimeout;
|
||||
set => SetProperty(ref _sendTimeout, value);
|
||||
}
|
||||
|
||||
private int _receiveTimeout = 3000;
|
||||
public int ReceiveTimeout
|
||||
{
|
||||
get => _receiveTimeout;
|
||||
set => SetProperty(ref _receiveTimeout, value);
|
||||
}
|
||||
|
||||
private bool _isConnected;
|
||||
public bool IsConnected
|
||||
{
|
||||
get => _isConnected;
|
||||
set => SetProperty(ref _isConnected, value);
|
||||
}
|
||||
|
||||
private string _connectionStatus = "未连接";
|
||||
public string ConnectionStatus
|
||||
{
|
||||
get => _connectionStatus;
|
||||
set => SetProperty(ref _connectionStatus, value);
|
||||
}
|
||||
|
||||
private string _errorMessage = string.Empty;
|
||||
public string ErrorMessage
|
||||
{
|
||||
get => _errorMessage;
|
||||
set => SetProperty(ref _errorMessage, value);
|
||||
}
|
||||
|
||||
public ObservableCollection<int> CommonPorts { get; } = new()
|
||||
{
|
||||
502, 102, 80, 8080, 5020, 4840
|
||||
};
|
||||
|
||||
public ObservableCollection<int> CommonTimeouts { get; } = new()
|
||||
{
|
||||
500, 1000, 2000, 3000, 5000, 10000
|
||||
};
|
||||
|
||||
#endregion
|
||||
|
||||
#region 命令
|
||||
public ICommand ConnectCommand { get; }
|
||||
public ICommand DisconnectCommand { get; }
|
||||
public ICommand CloseCommand { get; }
|
||||
#endregion
|
||||
|
||||
private GantryControlTcp? _gantry;
|
||||
|
||||
public GantryConfigDialogViewModel(IContainerProvider containerProvider) : base(containerProvider)
|
||||
{
|
||||
ConnectCommand = new DelegateCommand(OnConnect);
|
||||
DisconnectCommand = new DelegateCommand(OnDisconnect);
|
||||
CloseCommand = new DelegateCommand(OnClose);
|
||||
}
|
||||
|
||||
private void ApplyConfigToDevice()
|
||||
{
|
||||
if (_gantry?.TcpDevice == null) return;
|
||||
_gantry.TcpDevice.ConfigureDevice(IpAddress, Port, SendTimeout, ReceiveTimeout);
|
||||
}
|
||||
|
||||
private async void OnConnect()
|
||||
{
|
||||
ErrorMessage = string.Empty;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(IpAddress))
|
||||
{
|
||||
ErrorMessage = "IP 地址不能为空";
|
||||
return;
|
||||
}
|
||||
if (!System.Net.IPAddress.TryParse(IpAddress, out _))
|
||||
{
|
||||
ErrorMessage = "IP 地址格式不正确";
|
||||
return;
|
||||
}
|
||||
if (Port <= 0 || Port > 65535)
|
||||
{
|
||||
ErrorMessage = "端口范围应在 1 - 65535";
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
ConnectionStatus = "连接中...";
|
||||
ApplyConfigToDevice();
|
||||
bool ok = await _gantry!.ConnectAsync();
|
||||
if (ok)
|
||||
{
|
||||
IsConnected = true;
|
||||
ConnectionStatus = "已连接";
|
||||
LoggerHelper.Info($"[BenchMovement] 台架连接成功: {IpAddress}:{Port}");
|
||||
}
|
||||
else
|
||||
{
|
||||
ConnectionStatus = "连接失败";
|
||||
ErrorMessage = "连接失败,请检查网络和设备状态";
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ConnectionStatus = "连接失败";
|
||||
ErrorMessage = $"连接异常: {ex.Message}";
|
||||
LoggerHelper.Error($"[BenchMovement] 连接异常: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDisconnect()
|
||||
{
|
||||
if (_gantry == null) return;
|
||||
try
|
||||
{
|
||||
_gantry.Close();
|
||||
IsConnected = false;
|
||||
ConnectionStatus = "未连接";
|
||||
ErrorMessage = string.Empty;
|
||||
LoggerHelper.Info("[BenchMovement] 台架已断开");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ErrorMessage = $"断开失败: {ex.Message}";
|
||||
LoggerHelper.Error($"[BenchMovement] 断开失败: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private void OnClose()
|
||||
{
|
||||
RequestClose.Invoke(ButtonResult.OK);
|
||||
}
|
||||
|
||||
#region Prism Dialog 规范
|
||||
|
||||
public override void OnDialogOpened(IDialogParameters parameters)
|
||||
{
|
||||
_eventAggregator.GetEvent<UIShare.PubEvent.OverlayEvent>().Publish(true);
|
||||
|
||||
if (parameters.ContainsKey("Gantry"))
|
||||
{
|
||||
_gantry = parameters.GetValue<GantryControlTcp>("Gantry");
|
||||
|
||||
// 从现有设备读取当前配置
|
||||
if (_gantry?.TcpDevice != null)
|
||||
{
|
||||
IpAddress = _gantry.TcpDevice.IPAddress;
|
||||
Port = _gantry.TcpDevice.Port;
|
||||
SendTimeout = _gantry.TcpDevice.SendTimeout;
|
||||
ReceiveTimeout = _gantry.TcpDevice.ReceiveTimeout;
|
||||
}
|
||||
|
||||
// 同步当前连接状态
|
||||
IsConnected = _gantry?.IsConnected ?? false;
|
||||
ConnectionStatus = IsConnected ? "已连接" : "未连接";
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnDialogClosed()
|
||||
{
|
||||
_eventAggregator.GetEvent<UIShare.PubEvent.OverlayEvent>().Publish(false);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -195,8 +195,7 @@
|
||||
</Ellipse.Style>
|
||||
</Ellipse>
|
||||
<TextBlock Text="{Binding ConnectionStatus}" FontSize="16" FontWeight="Bold" VerticalAlignment="Center" Margin="0,0,20,0"/>
|
||||
<Button Content="配置连接" Width="100" Margin="0" Command="{Binding OpenConfigCommand}"/>
|
||||
<Button Content="连接" Width="80" Margin="8,0,0,0" Style="{StaticResource ConnectButtonStyle}" Command="{Binding ConnectCommand}"/>
|
||||
<Button Content="连接" Width="80" Margin="0" Style="{StaticResource ConnectButtonStyle}" Command="{Binding ConnectCommand}"/>
|
||||
<Button Content="断开" Width="80" Margin="8,0,0,0" Style="{StaticResource StopButtonStyle}" Command="{Binding DisconnectCommand}"/>
|
||||
<Button Content="刷新状态" Width="90" Margin="8,0,0,0" Command="{Binding ReadStatusCommand}"/>
|
||||
</StackPanel>
|
||||
|
||||
@@ -1,140 +0,0 @@
|
||||
<UserControl x:Class="BenchMovementModule.Views.Dialogs.GantryConfigDialogView"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:helpers="clr-namespace:UIShare.Helpers;assembly=UIShare"
|
||||
xmlns:converters="clr-namespace:UIShare.Converters;assembly=UIShare"
|
||||
mc:Ignorable="d"
|
||||
xmlns:prism="http://prismlibrary.com/"
|
||||
Background="White"
|
||||
prism:ViewModelLocator.AutoWireViewModel="True"
|
||||
Width="440"
|
||||
Height="380">
|
||||
<prism:Dialog.WindowStyle>
|
||||
<Style BasedOn="{StaticResource DialogUserManageStyle}"
|
||||
TargetType="Window" />
|
||||
</prism:Dialog.WindowStyle>
|
||||
|
||||
<UserControl.Resources>
|
||||
<converters:StringToVisibilityConverter x:Key="StringToVisibility"/>
|
||||
</UserControl.Resources>
|
||||
|
||||
<GroupBox Padding="12,8,12,8"
|
||||
helpers:WindowDragHelper.EnableWindowDrag="True">
|
||||
<GroupBox.Header>
|
||||
<Grid Margin="0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Text="{Binding Title}"
|
||||
Foreground="White"
|
||||
VerticalAlignment="Center"
|
||||
Margin="5,0,10,0" />
|
||||
</Grid>
|
||||
</GroupBox.Header>
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!-- 连接配置表单 -->
|
||||
<Grid Grid.Row="0" Margin="0,4,0,0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="120"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<TextBlock Grid.Row="0" Grid.Column="0"
|
||||
Text="IP 地址:"
|
||||
VerticalAlignment="Center" Margin="0,6"/>
|
||||
<TextBox Grid.Row="0" Grid.Column="1" Margin="0,6"
|
||||
materialDesign:HintAssist.Hint=""
|
||||
Text="{Binding IpAddress, UpdateSourceTrigger=PropertyChanged}"/>
|
||||
|
||||
<TextBlock Grid.Row="1" Grid.Column="0"
|
||||
Text="端口:"
|
||||
VerticalAlignment="Center" Margin="0,6"/>
|
||||
<ComboBox Grid.Row="1" Grid.Column="1" Margin="0,6"
|
||||
IsEditable="True"
|
||||
materialDesign:HintAssist.Hint=""
|
||||
ItemsSource="{Binding CommonPorts}"
|
||||
Text="{Binding Port, UpdateSourceTrigger=PropertyChanged}"/>
|
||||
|
||||
<TextBlock Grid.Row="2" Grid.Column="0"
|
||||
Text="发送超时(ms):"
|
||||
VerticalAlignment="Center" Margin="0,6"/>
|
||||
<ComboBox Grid.Row="2" Grid.Column="1" Margin="0,6"
|
||||
IsEditable="True"
|
||||
materialDesign:HintAssist.Hint=""
|
||||
ItemsSource="{Binding CommonTimeouts}"
|
||||
Text="{Binding SendTimeout, UpdateSourceTrigger=PropertyChanged}"/>
|
||||
|
||||
<TextBlock Grid.Row="3" Grid.Column="0"
|
||||
Text="接收超时(ms):"
|
||||
VerticalAlignment="Center" Margin="0,6"/>
|
||||
<ComboBox Grid.Row="3" Grid.Column="1" Margin="0,6"
|
||||
IsEditable="True"
|
||||
materialDesign:HintAssist.Hint=""
|
||||
ItemsSource="{Binding CommonTimeouts}"
|
||||
Text="{Binding ReceiveTimeout, UpdateSourceTrigger=PropertyChanged}"/>
|
||||
</Grid>
|
||||
|
||||
<!-- 连接状态 -->
|
||||
<StackPanel Grid.Row="1" Orientation="Horizontal" Margin="0,8,0,0">
|
||||
<Ellipse Width="12" Height="12" Margin="0,0,6,0">
|
||||
<Ellipse.Style>
|
||||
<Style TargetType="Ellipse">
|
||||
<Setter Property="Fill" Value="#999999"/>
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding IsConnected}" Value="True">
|
||||
<Setter Property="Fill" Value="#5CB85C"/>
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</Ellipse.Style>
|
||||
</Ellipse>
|
||||
<TextBlock Text="{Binding ConnectionStatus}" FontSize="14" FontWeight="Bold" VerticalAlignment="Center"/>
|
||||
</StackPanel>
|
||||
|
||||
<!-- 错误提示 -->
|
||||
<TextBlock Grid.Row="2"
|
||||
Margin="0,8,0,0"
|
||||
Foreground="#D32F2F"
|
||||
TextWrapping="Wrap"
|
||||
Text="{Binding ErrorMessage}"
|
||||
Visibility="{Binding ErrorMessage, Converter={StaticResource StringToVisibility}}"/>
|
||||
|
||||
<!-- 按钮区 -->
|
||||
<StackPanel Grid.Row="3"
|
||||
Orientation="Horizontal"
|
||||
HorizontalAlignment="Right"
|
||||
Margin="0,12,0,0">
|
||||
<Button Content="连接台架"
|
||||
Width="100" Padding="0,4"
|
||||
Margin="0,0,10,0"
|
||||
Command="{Binding ConnectCommand}"/>
|
||||
<Button Content="断开台架"
|
||||
Width="100" Padding="0,4"
|
||||
Margin="0,0,10,0"
|
||||
Command="{Binding DisconnectCommand}"/>
|
||||
<Button Content="关闭"
|
||||
Width="80" Padding="0,4"
|
||||
IsDefault="True"
|
||||
Command="{Binding CloseCommand}"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</GroupBox>
|
||||
</UserControl>
|
||||
@@ -1,12 +0,0 @@
|
||||
using System.Windows.Controls;
|
||||
|
||||
namespace BenchMovementModule.Views.Dialogs
|
||||
{
|
||||
public partial class GantryConfigDialogView : UserControl
|
||||
{
|
||||
public GantryConfigDialogView()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user