using System;
using System.Globalization;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using TeamAAS.Global.Devices;
namespace TeamAAS.Motion
{
///
/// 运动卡通用调试页:轴选择 + 使能/回零/点动/定位/急停。
/// 仿真卡与后续真实卡共用;绑定 实例。
///
public partial class MotionDebugView : UserControl
{
private IMotionCard _motion;
private IAxis _selectedAxis;
private bool _jogActive;
private readonly System.Windows.Threading.DispatcherTimer _refreshTimer;
public MotionDebugView()
{
InitializeComponent();
_refreshTimer = new System.Windows.Threading.DispatcherTimer
{
Interval = TimeSpan.FromMilliseconds(200)
};
_refreshTimer.Tick += (_, __) => RefreshStatus();
Unloaded += (_, __) => _refreshTimer.Stop();
}
public void Bind(object motion)
{
_motion = motion as IMotionCard;
_selectedAxis = null;
_jogActive = false;
CmbAxis.ItemsSource = null;
if (_motion == null)
{
OpsRoot.IsEnabled = false;
_refreshTimer.Stop();
TxtStatus.Text = "---";
TxtMessage.Text = "未绑定运动卡";
ClearAxisMetrics();
SetConnDot(false);
return;
}
var axes = _motion.GetAxes();
CmbAxis.ItemsSource = axes;
if (axes != null && axes.Count > 0)
CmbAxis.SelectedIndex = 0;
OpsRoot.IsEnabled = _motion.IsConnected;
_refreshTimer.Start();
RefreshStatus();
}
private IAxis CurrentAxis
{
get
{
if (_selectedAxis != null) return _selectedAxis;
return CmbAxis.SelectedItem as IAxis;
}
}
private void CmbAxis_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
_selectedAxis = CmbAxis.SelectedItem as IAxis;
RefreshAxisInfo();
}
private double ParseDouble(TextBox box, double fallback)
{
return double.TryParse(box.Text, NumberStyles.Any, CultureInfo.InvariantCulture, out var v)
? v
: fallback;
}
private void Msg(string message) => TxtMessage.Text = message;
private bool TryGetReadyAxis(out IAxis axis, string action)
{
axis = CurrentAxis;
if (axis == null)
{
Msg("未选择轴");
return false;
}
if (_motion == null || !_motion.IsConnected)
{
Msg("运动卡未连接");
return false;
}
if (!axis.IsEnabled)
{
Msg($"轴未使能,无法{action}");
return false;
}
return true;
}
private void RefreshStatus()
{
if (_motion == null)
{
TxtStatus.Text = "---";
SetConnDot(false);
ClearAxisMetrics();
return;
}
try
{
var connected = _motion.IsConnected;
TxtStatus.Text = connected ? "已连接" : "未连接";
SetConnDot(connected);
OpsRoot.IsEnabled = connected;
RefreshAxisInfo();
}
catch { }
}
private void RefreshAxisInfo()
{
var axis = CurrentAxis;
if (axis == null)
{
ClearAxisMetrics();
return;
}
TxtAxisTitle.Text = $"{axis.Name} · 轴 {axis.AxisNo}";
TxtPosActual.Text = axis.ActualPosition.ToString("F3");
TxtPosCommand.Text = axis.CommandPosition.ToString("F3");
TxtVelocity.Text = axis.ActualVelocity.ToString("F3");
ApplyBanner(axis);
SetTile(TileEnable, TxtEnable, axis.IsEnabled, "已使能", "未使能", "#2E7D32", "#C62828");
SetTile(TileMoving, TxtMoving, axis.IsMoving, "运动中", "静止", "#EF6C00", "#757575");
SetTile(TileHomed, TxtHomed, axis.IsHomed, "已回零", "未回零", "#1565C0", "#757575");
var enabled = axis.IsEnabled;
BtnEnable.IsEnabled = !enabled;
BtnDisable.IsEnabled = enabled;
BtnStop.IsEnabled = axis.IsMoving;
MotionOps.IsEnabled = enabled;
TxtNeedEnable.Visibility = enabled ? Visibility.Collapsed : Visibility.Visible;
}
private void ApplyBanner(IAxis axis)
{
string headline;
string hint;
string color;
if (_motion == null || !_motion.IsConnected)
{
headline = "未连接";
hint = "请先在上方连接控制卡";
color = "#616161";
}
else if (axis.State == AxisState.Error)
{
headline = "轴故障";
hint = "请检查报警后重新使能";
color = "#B71C1C";
}
else if (!axis.IsEnabled || axis.State == AxisState.Off)
{
headline = "伺服未使能";
hint = "当前无法运动,请先点击右侧「使能」";
color = "#C62828";
}
else if (axis.State == AxisState.Homing)
{
headline = "正在回零";
hint = "回零过程中请勿点动或定位";
color = "#1565C0";
}
else if (axis.State == AxisState.ContinuousMotion || (axis.IsMoving && _jogActive))
{
headline = "点动中";
hint = "松开按钮或点击「停止」即可停下";
color = "#EF6C00";
}
else if (axis.State == AxisState.DiscreteMotion || axis.IsMoving)
{
headline = "定位运动中";
hint = "可点击「停止」或「急停」中断";
color = "#EF6C00";
}
else
{
headline = axis.IsHomed ? "已使能 · 静止" : "已使能 · 未回零";
hint = axis.IsHomed ? "可以点动或定位" : "建议先回零,再做定位";
color = "#2E7D32";
}
TxtStateHeadline.Text = headline;
TxtStateHint.Text = hint;
BannerState.Background = BrushOf(color);
}
private void ClearAxisMetrics()
{
TxtAxisTitle.Text = "---";
TxtPosActual.Text = "---";
TxtPosCommand.Text = "---";
TxtVelocity.Text = "---";
TxtStateHeadline.Text = _motion == null ? "未绑定运动卡" : (_motion.IsConnected ? "未选择轴" : "未连接");
TxtStateHint.Text = "请选择轴并连接控制卡";
BannerState.Background = BrushOf("#616161");
SetTile(TileEnable, TxtEnable, false, "已使能", "未使能", "#2E7D32", "#757575");
SetTile(TileMoving, TxtMoving, false, "运动中", "静止", "#EF6C00", "#757575");
SetTile(TileHomed, TxtHomed, false, "已回零", "未回零", "#1565C0", "#757575");
BtnEnable.IsEnabled = false;
BtnDisable.IsEnabled = false;
BtnStop.IsEnabled = false;
MotionOps.IsEnabled = false;
TxtNeedEnable.Visibility = Visibility.Collapsed;
}
private static void SetTile(Border tile, TextBlock label, bool on, string onText, string offText, string onColor, string offColor)
{
label.Text = on ? onText : offText;
tile.Background = BrushOf(on ? onColor : offColor);
}
private static SolidColorBrush BrushOf(string hex)
=> new SolidColorBrush((Color)ColorConverter.ConvertFromString(hex));
private void SetConnDot(bool connected)
{
DotConn.Fill = BrushOf(connected ? "#43A047" : "#9E9E9E");
}
private void BtnEnable_Click(object sender, RoutedEventArgs e)
{
var axis = CurrentAxis;
if (axis == null) { Msg("未选择轴"); return; }
try
{
axis.Enable();
Msg("已使能");
RefreshAxisInfo();
}
catch (Exception ex) { Msg("使能失败: " + ex.Message); }
}
private void BtnDisable_Click(object sender, RoutedEventArgs e)
{
var axis = CurrentAxis;
if (axis == null) { Msg("未选择轴"); return; }
try
{
StopJogIfActive();
axis.Disable();
Msg("已下使能");
RefreshAxisInfo();
}
catch (Exception ex) { Msg("下使能失败: " + ex.Message); }
}
private void BtnHome_Click(object sender, RoutedEventArgs e)
{
if (!TryGetReadyAxis(out var axis, "回零")) return;
try
{
StopJogIfActive();
axis.Home();
Msg("回零完成");
RefreshAxisInfo();
}
catch (Exception ex) { Msg("回零失败: " + ex.Message); }
}
private void BtnStop_Click(object sender, RoutedEventArgs e)
{
try
{
StopJogIfActive();
CurrentAxis?.Stop();
Msg("已停止");
RefreshAxisInfo();
}
catch (Exception ex) { Msg("停止失败: " + ex.Message); }
}
private void BtnEStop_Click(object sender, RoutedEventArgs e)
{
try
{
_jogActive = false;
_motion?.EmergencyStop();
Msg("急停已触发");
RefreshAxisInfo();
}
catch (Exception ex) { Msg("急停失败: " + ex.Message); }
}
private void BtnJog_Down(object sender, MouseButtonEventArgs e)
{
if (!TryGetReadyAxis(out var axis, "点动")) return;
var btn = sender as UIElement;
var tag = (sender as Button)?.Tag as string;
double vel = ParseDouble(TxtJogVel, 50);
if (tag == "-") vel = -vel;
try
{
axis.Jog(vel);
_jogActive = true;
btn?.CaptureMouse();
Msg($"点动 {tag} 速度 {vel:F1}");
}
catch (Exception ex) { Msg("点动失败: " + ex.Message); }
}
private void BtnJog_Up(object sender, MouseButtonEventArgs e)
{
var btn = sender as UIElement;
if (btn != null && btn.IsMouseCaptured)
btn.ReleaseMouseCapture();
else
StopJogIfActive();
}
private void BtnJog_LostCapture(object sender, MouseEventArgs e)
{
StopJogIfActive();
}
private void StopJogIfActive()
{
if (!_jogActive) return;
_jogActive = false;
try
{
CurrentAxis?.Stop();
Msg("点动停止");
RefreshAxisInfo();
}
catch (Exception ex) { Msg("停止失败: " + ex.Message); }
}
private void BtnMoveAbs_Click(object sender, RoutedEventArgs e)
{
if (!TryGetReadyAxis(out var axis, "定位")) return;
try
{
var target = ParseDouble(TxtTargetPos, 0);
axis.Velocity = ParseDouble(TxtMoveVel, 100);
axis.MoveTo(target);
Msg($"绝对定位 → {target:F3}");
}
catch (Exception ex) { Msg("绝对定位失败: " + ex.Message); }
}
private void BtnMoveRel_Click(object sender, RoutedEventArgs e)
{
if (!TryGetReadyAxis(out var axis, "定位")) return;
try
{
var dist = ParseDouble(TxtRelativeDist, 10);
axis.Velocity = ParseDouble(TxtMoveVel, 100);
axis.MoveBy(dist);
Msg($"相对定位 {dist:F3}");
}
catch (Exception ex) { Msg("相对定位失败: " + ex.Message); }
}
private void BtnSetZero_Click(object sender, RoutedEventArgs e)
{
var axis = CurrentAxis;
if (axis == null) { Msg("未选择轴"); return; }
try
{
axis.SetPosition(0);
Msg("当前位置已清零");
RefreshAxisInfo();
}
catch (Exception ex) { Msg("清零失败: " + ex.Message); }
}
}
/// 运动卡通用调试页工厂。
public class MotionDebugPageFactory : TeamAAS.Global.Devices.IDeviceDebugPageFactory
{
public string DeviceCategory => "运动卡";
public object CreatePage(object device)
{
var view = new MotionDebugView();
view.Bind(device);
return view;
}
}
}