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; } } }