浏览代码

新增XYZ/XYZU平台附加轴手动调试与点动功能

本次提交实现了XYZ/XYZU平台机器人“附加轴”手动调试与点动控制,包括:
- 新增PlcRobotManualStep界面及ViewModel,支持附加轴点动、上/下使能、复位等操作;
- AxisExtended类扩展,XYZU_Robot增加附加轴集合及相关操作方法;
- 调试界面自动区分XYZ/XYZU平台,切换至新手动界面;
- 优化运动控制区与附加轴操作区布局,支持多轴动态扩展;
- 删除AxisViewModel.cs,完善类型判断与数据绑定;
- 同步更新XAML与csproj文件,确保新功能集成。

该改动显著提升了多轴系统的调试效率和操作体验。
孝锋 徐 9 月之前
父节点
当前提交
342cfb49ee

+ 1 - 0
TeamAAS-VM/App.xaml.cs

@@ -69,6 +69,7 @@ namespace TeamAAS_VP
             containerRegistry.RegisterForNavigation<Views.User.LoginView>();
             containerRegistry.RegisterForNavigation<SignUpView>();
             containerRegistry.RegisterForNavigation<RobotManualStep>();
+            containerRegistry.RegisterForNavigation<PlcRobotManualStep>();
             containerRegistry.RegisterForNavigation<FeederManual>();
             containerRegistry.RegisterForNavigation<VisionSolution>();
             containerRegistry.RegisterForNavigation<InchingControl>();

+ 23 - 6
TeamAAS-VM/Controls/PlcRobotManual.xaml

@@ -16,6 +16,7 @@
              lex:ResxLocalizationProvider.DefaultDictionary="Lang"
              mc:Ignorable="d"
              d:Background="White"
+             d:DataContext="{d:DesignInstance Type=local:PlcRobotManual}"
              FontFamily="{DynamicResource DefaultFont}">
     <UserControl.Resources>
         <convert:JogConverter x:Key="JogConverter" />
@@ -257,7 +258,8 @@
             </Grid>
             
             <!-- 附加轴列表:绑定到视图模型中的 ExtraAxes 集合(每项假定有 Name、Position) -->
-            <ItemsControl Grid.Row="1" ItemsSource="{Binding ExtraAxes}">
+            <ItemsControl Grid.Row="1"
+                          ItemsSource="{Binding AdditionalAxisList}">
                 <ItemsControl.ItemTemplate>
                     <DataTemplate>
                         <Border Padding="6"
@@ -271,8 +273,9 @@
                                     <TextBlock Text="{Binding Name}"
                                                Width="140"
                                                VerticalAlignment="Center" />
-                                    <TextBlock Text="{Binding Position, StringFormat='{}{0:N3} mm'}"
+                                    <TextBlock Text="{Binding CurrentPosition, StringFormat='{}{0:F3} mm'}"
                                                Width="140"
+                                               Margin="10,0"
                                                VerticalAlignment="Center" />
                                 </StackPanel>
                                 <StackPanel Orientation="Horizontal"
@@ -283,15 +286,29 @@
                                             Height="36"
                                             Margin="6,0"
                                             Style="{StaticResource MaterialDesignRaisedDarkButton}"
-                                            Command="{Binding DataContext.JogCommand, RelativeSource={RelativeSource AncestorType=UserControl}}"
-                                            CommandParameter="{Binding Name, StringFormat={}{0}-}" />
+                                            Command="{Binding DataContext.AdditionalAxisJogPlusCommand, RelativeSource={RelativeSource AncestorType=UserControl}}"
+                                            CommandParameter="{Binding}" />
                                     <Button Content="+"
                                             Width="60"
                                             Height="36"
                                             Margin="6,0"
                                             Style="{StaticResource MaterialDesignRaisedDarkButton}"
-                                            Command="{Binding DataContext.JogCommand, RelativeSource={RelativeSource AncestorType=UserControl}}"
-                                            CommandParameter="{Binding Name, StringFormat={}{0}+}" />
+                                            Command="{Binding DataContext.AdditionalAxisJogMinusCommand, RelativeSource={RelativeSource AncestorType=UserControl}}"
+                                            CommandParameter="{Binding}" />
+                                    <Button Content="上使能"
+                                            Width="60"
+                                            Height="36"
+                                            Margin="6,0"
+                                            Style="{StaticResource MaterialDesignRaisedDarkButton}"
+                                            Command="{Binding DataContext.AdditionalAxisMotorOnCommand, RelativeSource={RelativeSource AncestorType=UserControl}}"
+                                            CommandParameter="{Binding}" />
+                                    <Button Content="下使能"
+                                            Width="60"
+                                            Height="36"
+                                            Margin="6,0"
+                                            Style="{StaticResource MaterialDesignRaisedDarkButton}"
+                                            Command="{Binding DataContext.AdditionalAxisMotorOffCommand, RelativeSource={RelativeSource AncestorType=UserControl}}"
+                                            CommandParameter="{Binding}" />
                                 </StackPanel>
                             </DockPanel>
                         </Border>

+ 128 - 2
TeamAAS-VM/Controls/PlcRobotManual.xaml.cs

@@ -1,5 +1,6 @@
 using Prism.Commands;
 using Prism.Ioc;
+using Prism.Mvvm;
 using Prism.Unity;
 using System;
 using System.Collections.Generic;
@@ -38,8 +39,10 @@ namespace TeamAAS_VP.Controls
         }
 
         #region 属性
-        private ObservableCollection<Axis> _AdditionalAxisList;
-        public ObservableCollection<Axis> AdditionalAxisList
+
+
+        private ObservableCollection<AxisExtended> _AdditionalAxisList;
+        public ObservableCollection<AxisExtended> AdditionalAxisList
         {
             get { return _AdditionalAxisList; }
             set { SetProperty(ref _AdditionalAxisList, value); }
@@ -113,6 +116,24 @@ namespace TeamAAS_VP.Controls
         public DelegateCommand<object> JogCommand =>
             _JogCommand ?? (_JogCommand = new DelegateCommand<object>(ExecuteJogCommand).ObservesCanExecute(() => CanExecute).ObservesProperty(() => Robot).ObservesProperty(() => Robot.CanExecute));
 
+        //附加轴,轴点动+命令
+        private DelegateCommand<object> _AdditionalAxisJogPlusCommand;
+        public DelegateCommand<object> AdditionalAxisJogPlusCommand =>
+            _AdditionalAxisJogPlusCommand ?? (_AdditionalAxisJogPlusCommand = new DelegateCommand<object>(ExecuteAdditionalAxisJogPlusCommand).ObservesCanExecute(() => CanExecute).ObservesProperty(() => Robot).ObservesProperty(() => Robot.CanExecute));
+
+        //附加轴,轴点动-命令
+        private DelegateCommand<object> _AdditionalAxisJogMinusCommand;
+        public DelegateCommand<object> AdditionalAxisJogMinusCommand =>
+            _AdditionalAxisJogMinusCommand ?? (_AdditionalAxisJogMinusCommand = new DelegateCommand<object>(ExecuteAdditionalAxisJogMinusCommand).ObservesCanExecute(() => CanExecute).ObservesProperty(() => Robot).ObservesProperty(() => Robot.CanExecute));
+
+        private DelegateCommand<object> _AdditionalAxisMotorOnCommand;
+        public DelegateCommand<object> AdditionalAxisMotorOnCommand =>
+            _AdditionalAxisMotorOnCommand ?? (_AdditionalAxisMotorOnCommand = new DelegateCommand<object>(ExecuteAdditionalAxisMotorOnCommand).ObservesCanExecute(() => CanExecute).ObservesProperty(() => Robot).ObservesProperty(() => Robot.CanExecute));
+
+        private DelegateCommand<object> _AdditionalAxisMotorOffCommand;
+        public DelegateCommand<object> AdditionalAxisMotorOffCommand =>
+            _AdditionalAxisMotorOffCommand ?? (_AdditionalAxisMotorOffCommand = new DelegateCommand<object>(ExecuteAdditionalAxisMotorOffCommand).ObservesCanExecute(() => CanExecute).ObservesProperty(() => Robot).ObservesProperty(() => Robot.CanExecute));
+
         #endregion
 
         #region 事件
@@ -263,6 +284,111 @@ namespace TeamAAS_VP.Controls
                 }
             }
         }
+
+        /// <summary>
+        /// 附加轴+点动
+        /// </summary>
+        /// <param name="obj"></param>
+        async void ExecuteAdditionalAxisJogPlusCommand(object obj)
+        {
+            if (Robot == null || !Robot.IsConnected) return;
+            try
+            {
+                if (obj is AxisExtended axis)
+                {
+                    //如果移动距离大于10mm,则提示用户确认
+                    if (Math.Abs(Distance) >= 10)
+                    {
+                        if (MessageBox.Show($"确认要点动距离 {Distance} mm 吗?", "确认点动", MessageBoxButton.YesNo, MessageBoxImage.Question) != MessageBoxResult.Yes)
+                        {
+                            return;
+                        }
+                    }
+                    await Robot.AdditionalAxisJogAsync(axis.Name, Distance);
+                    ShowMsg($"{Lang.完成}!");
+                }
+            }
+            catch (Exception ex)
+            {
+                LogHelper.WriteLogError("附加轴点动+时出错!", ex);
+                ShowMsg(ex.Message);
+            }
+        }
+
+        /// <summary>
+        /// 附加轴-点动
+        /// </summary>
+        /// <param name="obj"></param>
+        async void ExecuteAdditionalAxisJogMinusCommand(object obj)
+        {
+            if (Robot == null || !Robot.IsConnected) return;
+            try
+            {
+                if (obj is AxisExtended axis)
+                {
+                    //如果移动距离大于10mm,则提示用户确认
+                    if (Math.Abs(Distance) >= 10)
+                    {
+                        if (MessageBox.Show($"确认要点动距离 {Distance} mm 吗?", "确认点动", MessageBoxButton.YesNo, MessageBoxImage.Question) != MessageBoxResult.Yes)
+                        {
+                            return;
+                        }
+                    }
+                    await Robot.AdditionalAxisJogNegAsync(axis.Name, Distance);
+                    ShowMsg($"{Lang.完成}!");
+                }
+            }
+            catch (Exception ex)
+            {
+                LogHelper.WriteLogError("附加轴点动-时出错!", ex);
+                ShowMsg(ex.Message);
+            }
+        }
+
+        /// <summary>
+        /// 附加轴上使能
+        /// </summary>
+        /// <param name="obj"></param>
+        async void ExecuteAdditionalAxisMotorOffCommand(object obj)
+        {
+            if (Robot == null || !Robot.IsConnected) return;
+            try
+            {
+                if (obj is AxisExtended axis)
+                {
+                    await Robot.AdditionalAxisMotorAsync(axis.Name, true);
+                    ShowMsg($"{Lang.完成}!");
+                }
+            }
+            catch (Exception ex)
+            {
+                LogHelper.WriteLogError("附加轴上使能时出错!", ex);
+                ShowMsg(ex.Message);
+            }
+        }
+
+        /// <summary>
+        /// 附加轴下使能
+        /// </summary>
+        /// <param name="obj"></param>
+        async void ExecuteAdditionalAxisMotorOnCommand(object obj)
+        {
+            if (Robot == null || !Robot.IsConnected) return;
+            try
+            {
+                if (obj is AxisExtended axis)
+                {
+                    await Robot.AdditionalAxisMotorAsync(axis.Name, true);
+                    ShowMsg($"{Lang.完成}!");
+                }
+            }
+            catch (Exception ex)
+            {
+                LogHelper.WriteLogError("附加轴下使能时出错!", ex);
+                ShowMsg(ex.Message);
+            }
+        }
+
         #endregion
 
         #region 属性通知

+ 87 - 0
TeamAAS-VM/Core/Robots/Axis.cs

@@ -1,6 +1,8 @@
 using System;
 using System.Collections.Generic;
+using System.ComponentModel;
 using System.Linq;
+using System.Runtime.CompilerServices;
 using System.Text;
 using System.Threading.Tasks;
 using System.Web;
@@ -206,4 +208,89 @@ namespace TeamAAS_VP.Core.Robots
 
         public AxisState State { get; set; }
     }
+
+    public class AxisExtended : Axis, INotifyPropertyChanged
+    {
+        //µ±Ç°×ø±ê
+        private double _CurrentPosition;
+        public double CurrentPosition
+        {
+            get { return _CurrentPosition; }
+            set { SetProperty(ref _CurrentPosition, value); }
+        }
+
+        #region ÊôÐÔ֪ͨ
+        /// <summary>
+        /// Occurs when a property value changes.
+        /// </summary>
+        public event PropertyChangedEventHandler PropertyChanged;
+
+        /// <summary>
+        /// Checks if a property already matches a desired value. Sets the property and
+        /// notifies listeners only when necessary.
+        /// </summary>
+        /// <typeparam name="T">Type of the property.</typeparam>
+        /// <param name="storage">Reference to a property with both getter and setter.</param>
+        /// <param name="value">Desired value for the property.</param>
+        /// <param name="propertyName">Name of the property used to notify listeners. This
+        /// value is optional and can be provided automatically when invoked from compilers that
+        /// support CallerMemberName.</param>
+        /// <returns>True if the value was changed, false if the existing value matched the
+        /// desired value.</returns>
+        protected virtual bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
+        {
+            if (EqualityComparer<T>.Default.Equals(storage, value)) return false;
+
+            storage = value;
+            RaisePropertyChanged(propertyName);
+
+            return true;
+        }
+
+        /// <summary>
+        /// Checks if a property already matches a desired value. Sets the property and
+        /// notifies listeners only when necessary.
+        /// </summary>
+        /// <typeparam name="T">Type of the property.</typeparam>
+        /// <param name="storage">Reference to a property with both getter and setter.</param>
+        /// <param name="value">Desired value for the property.</param>
+        /// <param name="propertyName">Name of the property used to notify listeners. This
+        /// value is optional and can be provided automatically when invoked from compilers that
+        /// support CallerMemberName.</param>
+        /// <param name="onChanged">Action that is called after the property value has been changed.</param>
+        /// <returns>True if the value was changed, false if the existing value matched the
+        /// desired value.</returns>
+        protected virtual bool SetProperty<T>(ref T storage, T value, Action onChanged, [CallerMemberName] string propertyName = null)
+        {
+            if (EqualityComparer<T>.Default.Equals(storage, value)) return false;
+
+            storage = value;
+            onChanged?.Invoke();
+            RaisePropertyChanged(propertyName);
+
+            return true;
+        }
+
+        /// <summary>
+        /// Raises this object's PropertyChanged event.
+        /// </summary>
+        /// <param name="propertyName">Name of the property used to notify listeners. This
+        /// value is optional and can be provided automatically when invoked from compilers
+        /// that support <see cref="CallerMemberNameAttribute"/>.</param>
+        protected void RaisePropertyChanged([CallerMemberName] string propertyName = null)
+        {
+            OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
+        }
+
+        /// <summary>
+        /// Raises this object's PropertyChanged event.
+        /// </summary>
+        /// <param name="args">The PropertyChangedEventArgs</param>
+        protected virtual void OnPropertyChanged(PropertyChangedEventArgs args)
+        {
+            PropertyChanged?.Invoke(this, args);
+        }
+        #endregion
+    }
+
 }

+ 0 - 187
TeamAAS-VM/Core/Robots/AxisViewModel.cs

@@ -1,187 +0,0 @@
-
-using System;
-using System.ComponentModel;
-using System.Threading.Tasks;
-using Prism.Commands;
-using Prism.Mvvm;
-using TeamAAS_VP.Interfaces;
-using TeamAAS_VP.Models;
-
-namespace TeamAAS_VP.Core.Robots
-{
-    /// <summary>
-    /// Axis view-model for binding to View: provides Axis + / - commands, axis name and current position.
-    /// </summary>
-    public class AxisViewModel : BindableBase, IDisposable
-    {
-        private readonly IRobot _robot;
-        private readonly string _axisName;
-        private double _currentPosition;
-        private double _step;
-
-        public DelegateCommand AxisPlusCommand { get; private set; }
-        public DelegateCommand AxisMinusCommand { get; private set; }
-
-        /// <summary>
-        /// 轴名称(X/Y/Z/U)
-        /// </summary>
-        public string AxisName => _axisName;
-
-        /// <summary>
-        /// 当前轴位置(从 Robot 读取或命令后更新)
-        /// </summary>
-        public double CurrentPosition
-        {
-            get => _currentPosition;
-            private set => SetProperty(ref _currentPosition, value);
-        }
-
-        /// <summary>
-        /// 每次 jog 的步长(可设置)
-        /// </summary>
-        public double Step
-        {
-            get => _step;
-            set
-            {
-                if (SetProperty(ref _step, value))
-                {
-                    RaiseCanExecuteChanged();
-                }
-            }
-        }
-
-        public AxisViewModel(string axisName, IRobot robot, double step = 1.0)
-        {
-            if (string.IsNullOrWhiteSpace(axisName)) throw new ArgumentNullException(nameof(axisName));
-            _axisName = axisName.ToUpper();
-            _robot = robot ?? throw new ArgumentNullException(nameof(robot));
-            _step = step;
-
-            AxisPlusCommand = DelegateCommand.FromAsyncHandler(ExecutePlusAsync, CanExecuteCommands);
-            AxisMinusCommand = DelegateCommand.FromAsyncHandler(ExecuteMinusAsync, CanExecuteCommands);
-
-            // 尝试初始化当前位置(异步,不阻塞构造)
-            Task.Run(async () => await UpdatePositionFromRobotAsync());
-
-            // 订阅机器人属性变化以刷新命令可用性
-            if (_robot is INotifyPropertyChanged npc)
-            {
-                npc.PropertyChanged += Robot_PropertyChanged;
-            }
-        }
-
-        private void Robot_PropertyChanged(object sender, PropertyChangedEventArgs e)
-        {
-            // 监听 CanExecute 或 IsConnected 等关键属性名(Robots 中使用 CanExecute 字段)
-            if (string.Equals(e.PropertyName, nameof(XYZU_Robot.CanExecute), StringComparison.OrdinalIgnoreCase) ||
-                string.Equals(e.PropertyName, nameof(IRobot.CanExecute), StringComparison.OrdinalIgnoreCase) ||
-                string.Equals(e.PropertyName, nameof(IRobot.IsConnected), StringComparison.OrdinalIgnoreCase))
-            {
-                RaiseCanExecuteChanged();
-            }
-        }
-
-        private bool CanExecuteCommands()
-        {
-            try
-            {
-                if (_robot == null) return false;
-
-                // 优先使用机器人公开的 CanExecute 属性(若存在),否则回退到 IsConnected
-                var canExecuteProp = _robot.GetType().GetProperty("CanExecute");
-                if (canExecuteProp != null && canExecuteProp.PropertyType == typeof(bool))
-                {
-                    return (bool)canExecuteProp.GetValue(_robot);
-                }
-
-                return _robot.IsConnected;
-            }
-            catch
-            {
-                return false;
-            }
-        }
-
-        private void RaiseCanExecuteChanged()
-        {
-            AxisPlusCommand.RaiseCanExecuteChanged();
-            AxisMinusCommand.RaiseCanExecuteChanged();
-        }
-
-        private async Task ExecutePlusAsync()
-        {
-            if (!CanExecuteCommands()) return;
-
-            try
-            {
-                await _robot.JogAsync(_axisName, Step);
-            }
-            catch
-            {
-                // 忽略错误,视需求可扩展日志或通知
-            }
-
-            await UpdatePositionFromRobotAsync();
-        }
-
-        private async Task ExecuteMinusAsync()
-        {
-            if (!CanExecuteCommands()) return;
-
-            try
-            {
-                await _robot.JogAsync(_axisName, -Step);
-            }
-            catch
-            {
-                // 忽略错误
-            }
-
-            await UpdatePositionFromRobotAsync();
-        }
-
-        /// <summary>
-        /// 从 Robot 读取完整位置并更新当前轴的 CurrentPosition
-        /// </summary>
-        public async Task UpdatePositionFromRobotAsync()
-        {
-            try
-            {
-                var pos = await _robot.GetRobotPosAsync();
-                if (pos == null) return;
-
-                switch (_axisName)
-                {
-                    case "X":
-                        CurrentPosition = pos.X;
-                        break;
-                    case "Y":
-                        CurrentPosition = pos.Y;
-                        break;
-                    case "Z":
-                        CurrentPosition = pos.Z;
-                        break;
-                    case "U":
-                        CurrentPosition = pos.U;
-                        break;
-                    default:
-                        // 未知轴,保持不变
-                        break;
-                }
-            }
-            catch
-            {
-                // 忽略读取错误
-            }
-        }
-
-        public void Dispose()
-        {
-            if (_robot is INotifyPropertyChanged npc)
-            {
-                npc.PropertyChanged -= Robot_PropertyChanged;
-            }
-        }
-    }
-}

+ 314 - 1
TeamAAS-VM/Core/Robots/XYZU_Robot.cs

@@ -4,6 +4,7 @@ using Prism;
 using Prism.Ioc;
 using System;
 using System.Collections.Generic;
+using System.Collections.ObjectModel;
 using System.ComponentModel;
 using System.Diagnostics;
 using System.Linq;
@@ -17,6 +18,7 @@ using TeamAAS_VP.Interfaces;
 using TeamAAS_VP.Models;
 using TouchSocket.Core;
 using TouchSocket.Sockets;
+using static OpenCvSharp.ML.DTrees;
 using static System.Windows.Forms.AxHost;
 
 namespace TeamAAS_VP.Core.Robots
@@ -88,6 +90,24 @@ namespace TeamAAS_VP.Core.Robots
                 Brand = RobotBrand.XYZU_Platform;
             }
 
+            //附加轴集合
+            if(robot.PlcRobotParameter.AdditionalAxisList!=null && robot.PlcRobotParameter.AdditionalAxisList.Count>0)
+            {
+                AdditionalAxisList = new ObservableCollection<AxisExtended>();
+                foreach(var addAxis in robot.PlcRobotParameter.AdditionalAxisList)
+                {
+                    AxisExtended axisExt = new AxisExtended()
+                    {
+                        Name = addAxis.Name,
+                        Index = addAxis.Index,
+                        Command = addAxis.Command,
+                        Parameter = addAxis.Parameter,
+                        State = addAxis.State
+                    };
+                    AdditionalAxisList.Add(axisExt);
+                }
+            }
+
             if (Plc.IsConnected)
             {
                 try
@@ -115,6 +135,30 @@ namespace TeamAAS_VP.Core.Robots
                             }
                         }
                     });
+
+                    //所有附加轴的位置节点
+                    if (AdditionalAxisList != null && AdditionalAxisList.Count > 0)
+                    {
+                        var addNodeIds = AdditionalAxisList
+                            .Where(a => !string.IsNullOrWhiteSpace(a.State.ActPositionNode))
+                            .Select(a => a.State.ActPositionNode)
+                            .ToArray();
+                        List<string> addPositionNodes = new List<string>(addNodeIds);
+                        pLC.SubscribeNodes("AdditionalAxisPosition", addPositionNodes, (res) =>
+                        {
+                            if (res.key != "AdditionalAxisPosition") return;
+                            for (int i = 0; i < AdditionalAxisList.Count; i++)
+                            {
+                                var axis = AdditionalAxisList[i];
+                                if (axis.State.ActPositionNode.Contains(res.nodeId))
+                                {
+                                    axis.CurrentPosition = Convert.ToSingle(res.value);
+                                }
+                            }
+                        });
+                    }
+
+
                 }
                 catch (Exception)
                 {
@@ -191,6 +235,13 @@ namespace TeamAAS_VP.Core.Robots
 
         public List<Axis> Axes { get; private set; }
 
+        private ObservableCollection<AxisExtended> _AdditionalAxisList;
+        public ObservableCollection<AxisExtended> AdditionalAxisList
+        {
+            get { return _AdditionalAxisList; }
+            set { SetProperty(ref _AdditionalAxisList, value); }
+        }
+
         /// <summary>
         /// 进入调试模式
         /// </summary>
@@ -206,7 +257,6 @@ namespace TeamAAS_VP.Core.Robots
             get { return _CurrentPosition; }
             set { SetProperty(ref _CurrentPosition, value); }
         }
-
         #endregion
 
         #region 连接
@@ -776,6 +826,269 @@ namespace TeamAAS_VP.Core.Robots
             return (allOk, isAnyError);
         }
 
+        /// <summary>
+        /// 检查单个附加轴是否结束,返回是否停止、是否有错误
+        /// </summary>
+        /// <param name="axis"></param>
+        /// <param name="targetPos"></param>
+        /// <returns></returns>
+        private (bool isFinished, bool isError) CheckAdditionalAxisMoveFinished(AxisExtended axis, double targetPos)
+        {
+            //读取实际位置和到位状态
+            bool posOk = false;
+            float actualPos = 0;
+            bool isError = false;
+            var res = Plc.ReadNodes(new string[] { axis.State.PosOKNode, axis.State.ActPositionNode, axis.State.ErrorNode, axis.State.PausedNode });
+            if (res.ContainsKey(axis.State.PosOKNode))
+            {
+                posOk = (bool)res[axis.State.PosOKNode];
+            }
+            if (res.ContainsKey(axis.State.ActPositionNode))
+            {
+                try { actualPos = Convert.ToSingle(res[axis.State.ActPositionNode]); } catch { }
+            }
+            if (res.ContainsKey(axis.State.ErrorNode))
+            {
+                isError = (bool)res[axis.State.ErrorNode];
+            }
+            if (res.ContainsKey(axis.State.PausedNode))
+            {
+                isError = (bool)res[axis.State.PausedNode];
+            }
+            //检查是否到位和位置一致
+            if (isError)
+            {
+                return (true, true);
+            }
+            if (posOk && Math.Abs(actualPos - targetPos) < 0.02)
+            {
+                return (true, false);
+            }
+            return (false, false);
+        }
+
+        /// <summary>
+        /// 附加轴使能操作
+        /// </summary>
+        /// <param name="axisName"></param>
+        /// <param name="state"></param>
+        /// <returns></returns>
+        public bool AdditionalAxisMotor(string axisName, bool state)
+        {
+            var axis = AdditionalAxisList.FirstOrDefault(a => a.Name.Equals(axisName, StringComparison.OrdinalIgnoreCase));
+            if (axis == null) return false;
+            if (Plc == null || !Plc.IsConnected) return false;
+            try
+            {
+                if (!string.IsNullOrWhiteSpace(axis.Command.PowerNode))
+                {
+                    return Plc.WriteNode(axis.Command.PowerNode, state);
+                }
+                return false;
+            }
+            catch (Exception ex)
+            {
+                LogHelper.WriteLogError("执行附加轴使能操作时出错!", ex);
+                return false;
+            }
+        }
+
+        //附加轴使能操作,异步方法
+        public Task<bool> AdditionalAxisMotorAsync(string axisName, bool state)
+        {
+            return Task.Run(() => AdditionalAxisMotor(axisName, state));
+        }
+
+        /// <summary>
+        /// 附加轴,阻塞式等待运动完成
+        /// </summary>
+        /// <param name="axisName"></param>
+        /// <param name="targetPos"></param>
+        /// <returns></returns>
+        public bool AdditionalAxisMove(string axisName, double targetPos)
+        {
+            var axis = AdditionalAxisList.FirstOrDefault(a => a.Name.Equals(axisName, StringComparison.OrdinalIgnoreCase));
+            if (axis == null) return false;
+            if (Plc == null || !Plc.IsConnected) return false;
+            try
+            {
+                CanExecute = false;
+                AdditionalAxisMotor(axis.Name, true);
+                //写入目标位置
+                if (!string.IsNullOrWhiteSpace(axis.Parameter.ManuPositionNode))
+                {
+                    Plc.WriteNode(axis.Parameter.ManuPositionNode, (float)targetPos);
+                }
+                //开始移动
+                if (!string.IsNullOrWhiteSpace(axis.Command.ManuPositionNode))
+                {
+                    Plc.WriteNode(axis.Command.ManuPositionNode, true);
+                }
+
+                //准备停滞检测:收集所有实际位置节点
+                float lastPos = float.NaN;
+                DateTime lastChange = DateTime.UtcNow;
+                const float movementThreshold = 0.01f; // 判断位置变化的阈值,避免噪声
+                Stopwatch sw = new Stopwatch();
+                sw.Start();
+                
+                while (true)
+                {
+                    var now = DateTime.UtcNow;
+                    // 先读取轴的实际位置,用于停滞检测
+                    var actual = Plc.ReadNode<float>(axis.State.ActPositionNode);
+                    if (float.IsNaN(lastPos))
+                    {
+                        lastPos = actual;
+                        lastChange = now;
+                    }
+                    else
+                    {
+                        if (Math.Abs(actual - lastPos) > movementThreshold)
+                        {
+                            // 有实质性变化,更新记录时间和值
+                            lastPos = actual;
+                            lastChange = now;
+                        }
+                        // 否则保持 lastChange 不变(表示该轴最近一次变化的时间)
+                    }
+
+                    // 判断是否至少有一个轴在最近 Timeout 时间内发生过变化(即认为还在运动)
+                    bool anyAxisMovedRecently = (now - lastChange).TotalMilliseconds <= 1000;
+
+                    // 如果没有任何轴在最近 Timeout 时间内发生变化,则认为出现停滞异常
+                    if (!anyAxisMovedRecently)
+                    {
+                        //停止移动
+                        if (!string.IsNullOrWhiteSpace(axis.Command.ManuPositionNode))
+                        {
+                            Plc.WriteNode(axis.Command.ManuPositionNode, false);
+                        }
+                        return false;
+                    }
+
+                    // 检查是否整体到位或有错误(保留原有逻辑)
+                    var (isFinished, isError) = CheckAdditionalAxisMoveFinished(axis, targetPos);
+                    if (isFinished)
+                    {
+                        //停止移动
+                        if (!string.IsNullOrWhiteSpace(axis.Command.ManuPositionNode))
+                        {
+                            Plc.WriteNode(axis.Command.ManuPositionNode, false);
+                        }
+                        if (isError)
+                        {
+                            return false;
+                        }
+                        return true;
+                    }
+
+                    // 检查整体超时(防止一直有抖动导致 anyAxisMovedRecently 一直为 true)
+                    if (sw.ElapsedMilliseconds > 60000) // 保守超时,一分钟,可根据需求调整
+                    {
+                        //停止移动
+                        if (!string.IsNullOrWhiteSpace(axis.Command.ManuPositionNode))
+                        {
+                            Plc.WriteNode(axis.Command.ManuPositionNode, false);
+                        }
+                        return false;
+                    }
+
+                    Thread.Sleep(10);
+                }
+
+            }
+            catch (Exception ex)
+            {
+                LogHelper.WriteLogError("执行附加轴移动时出错!", ex);
+                return false;
+            }
+            finally
+            {
+                CanExecute = true;
+            }
+        }
+
+        /// <summary>
+        /// 附加轴,阻塞式等待运动完成,异步方法
+        /// </summary>
+        /// <param name="axisName"></param>
+        /// <param name="targetPos"></param>
+        /// <returns></returns>
+        public Task<bool> AdditionalAxisMoveAsync(string axisName, double targetPos)
+        {
+            return Task.Run(() => AdditionalAxisMove(axisName, targetPos));
+        }
+
+        /// <summary>
+        /// 附加轴Jog+点动
+        /// </summary>
+        /// <param name="axisName"></param>
+        /// <param name="distance"></param>
+        /// <returns></returns>
+        public bool AdditionalAxisJog(string axisName, double distance)
+        {
+            var axis = AdditionalAxisList.FirstOrDefault(a => a.Name.Equals(axisName, StringComparison.OrdinalIgnoreCase));
+            if (axis == null) return false;
+            
+            if (Plc == null || !Plc.IsConnected) return false;
+            try
+            {
+                CanExecute = false;
+
+                // 先读取轴的实际位置,用于停滞检测
+                var actual = Plc.ReadNode<float>(axis.State.ActPositionNode);
+                //计算目标位置
+                double targetPos = actual + distance;
+
+                AdditionalAxisMotor(axis.Name, true);
+
+                return AdditionalAxisMove(axis.Name, targetPos);
+            }
+            catch (Exception ex)
+            {
+                LogHelper.WriteLogError("执行附加轴点动时出错!", ex);
+                return false;
+            }
+            finally
+            {
+                CanExecute = true;
+            }
+        }
+
+        /// <summary>
+        /// 附加轴Jog-点动
+        /// </summary>
+        /// <param name="axisName"></param>
+        /// <param name="distance"></param>
+        /// <returns></returns>
+        public bool AdditionalAxisJogNeg(string axisName, double distance)
+        {
+            return AdditionalAxisJog(axisName, -distance);
+        }
+
+        /// <summary>
+        /// 附加轴异步 Jog+点动
+        /// </summary>
+        /// <param name="axisName"></param>
+        /// <param name="distance"></param>
+        /// <returns></returns>
+        public Task<bool> AdditionalAxisJogAsync(string axisName, double distance)
+        {
+            return Task.Run(() => AdditionalAxisJog(axisName, distance));
+        }
+
+        /// <summary>
+        /// 附加轴异步 Jog-点动
+        /// </summary>
+        /// <param name="axisName"></param>
+        /// <param name="distance"></param>
+        /// <returns></returns>
+        public Task<bool> AdditionalAxisJogNegAsync(string axisName, double distance)
+        {
+            return Task.Run(() => AdditionalAxisJogNeg(axisName, distance));
+        }
+
         #endregion
 
         #region 收发数据

+ 75 - 75
TeamAAS-VM/Services/CameraCalibrationService.cs

@@ -788,36 +788,36 @@ namespace TeamAAS_VP.Services
             await robot.CalibParameAsync(calibrationInfo.Pick, calibrationInfo.Speed, calibrationInfo.Accel, calibrationInfo.Power, calibrationInfo.WaitSuction, calibrationInfo.WaitBlow);
 
             var P0 = Vector<double>.Build.Dense(new double[] { calibrationInfo.CenterPoint.X, calibrationInfo.CenterPoint.Y });
-            //if (robot.Brand == Enums.RobotBrand.XYZ_Platform)
-            //{
-
-            //    //移动机器人至中心位置
-            //    await robot.CalibMotionAsync(calibrationInfo.CenterPoint, 0);
-            //    if (cancellationToken.IsCancellationRequested)
-            //    {
-            //        return (false, null, null, null);
-            //    }
-            //    //计算相机与机器人坐标系的旋转矩阵
-            //    var robotCameraRotationMatrix = await AutoIdentifyRobotCameraRotationMatrix(calibrationInfo, robot, cancellationToken);
-            //    if (!robotCameraRotationMatrix.IsSuccess)
-            //    {
-            //        return (false, null, null, null);
-            //    }
-            //    //自动移动机器人至图像中心点位置
-            //    var autoMoveResult = await AutoMoveRobotToImageCenter(robot, robotCameraRotationMatrix.RotationMatrix, robotCameraRotationMatrix.PixelScaleX, robotCameraRotationMatrix.PixelScaleY, cancellationToken);
-            //    if (!autoMoveResult.IsSuccess)
-            //    {
-            //        return (false, null, null, null);
-            //    }
-            //    calibrationInfo.CenterPoint.X = (float)autoMoveResult.X;
-            //    calibrationInfo.CenterPoint.Y = (float)autoMoveResult.Y;
-            //    if (calibrationInfo.CameraMount != Enums.CameraMount.MobileDown_XYPlatform)
-            //    {
-            //        calibrationInfo.MarkPoint = calibrationInfo.CenterPoint.Clone();
-            //    }
-                
-            //    calibrationInfo.RobotCameraRotationMatrix = robotCameraRotationMatrix.RotationMatrix.ToArray();
-            //}
+            if (robot.Brand == Enums.RobotBrand.XYZ_Platform)
+            {
+
+                //移动机器人至中心位置
+                await robot.CalibMotionAsync(calibrationInfo.CenterPoint, 0);
+                if (cancellationToken.IsCancellationRequested)
+                {
+                    return (false, null, null, null);
+                }
+                //计算相机与机器人坐标系的旋转矩阵
+                var robotCameraRotationMatrix = await AutoIdentifyRobotCameraRotationMatrix(calibrationInfo, robot, cancellationToken);
+                if (!robotCameraRotationMatrix.IsSuccess)
+                {
+                    return (false, null, null, null);
+                }
+                //自动移动机器人至图像中心点位置
+                var autoMoveResult = await AutoMoveRobotToImageCenter(robot, robotCameraRotationMatrix.RotationMatrix, robotCameraRotationMatrix.PixelScaleX, robotCameraRotationMatrix.PixelScaleY, cancellationToken);
+                if (!autoMoveResult.IsSuccess)
+                {
+                    return (false, null, null, null);
+                }
+                calibrationInfo.CenterPoint.X = (float)autoMoveResult.X;
+                calibrationInfo.CenterPoint.Y = (float)autoMoveResult.Y;
+                if (calibrationInfo.CameraMount != Enums.CameraMount.MobileDown_XYPlatform)
+                {
+                    calibrationInfo.MarkPoint = calibrationInfo.CenterPoint.Clone();
+                }
+
+                calibrationInfo.RobotCameraRotationMatrix = robotCameraRotationMatrix.RotationMatrix.ToArray();
+            }
 
             /// <summary>
             /// 机器人与相机之间的旋转矩阵
@@ -1077,14 +1077,14 @@ namespace TeamAAS_VP.Services
                     matRobot.Set<double>(i, 0, calibrationInfo.MarkPoint.X - ox);
                     matRobot.Set<double>(i, 1, calibrationInfo.MarkPoint.Y - oy);
                 }
-                //else if (calibrationInfo.CameraMount == Enums.CameraMount.MobileDown_XYPlatform)
-                //{
-                //    //计算每个点相对于第一个点移动的偏移量,将这个偏移量
-                //    double ox = rPoints[i].X - rPoints[0].X;
-                //    double oy = rPoints[i].Y - rPoints[0].Y;
-                //    matRobot.Set<double>(i, 0, calibrationInfo.MarkPoint.X - ox);
-                //    matRobot.Set<double>(i, 1, calibrationInfo.MarkPoint.Y - oy);
-                //}
+                else if (calibrationInfo.CameraMount == Enums.CameraMount.MobileDown_XYPlatform)
+                {
+                    //计算每个点相对于第一个点移动的偏移量,将这个偏移量
+                    double ox = rPoints[i].X - rPoints[0].X;
+                    double oy = rPoints[i].Y - rPoints[0].Y;
+                    matRobot.Set<double>(i, 0, calibrationInfo.MarkPoint.X - ox);
+                    matRobot.Set<double>(i, 1, calibrationInfo.MarkPoint.Y - oy);
+                }
                 else
                 {
                     //将tool 0下的坐标转换成Tool n下的坐标
@@ -1137,13 +1137,13 @@ namespace TeamAAS_VP.Services
                     differenceX.Add((calibrationInfo.MarkPoint.X - ox) - (matrix * pixel)[0]);
                     differenceY.Add((calibrationInfo.MarkPoint.Y - oy) - (matrix * pixel)[1]);
                 }
-                //else if (calibrationInfo.CameraMount == Enums.CameraMount.MobileDown_XYPlatform)
-                //{
-                //    double ox = rPoints[i].X - rPoints[0].X;
-                //    double oy = rPoints[i].Y - rPoints[0].Y;
-                //    differenceX.Add((calibrationInfo.MarkPoint.X - ox) - (matrix * pixel)[0]);
-                //    differenceY.Add((calibrationInfo.MarkPoint.Y - oy) - (matrix * pixel)[1]);
-                //}
+                else if (calibrationInfo.CameraMount == Enums.CameraMount.MobileDown_XYPlatform)
+                {
+                    double ox = rPoints[i].X - rPoints[0].X;
+                    double oy = rPoints[i].Y - rPoints[0].Y;
+                    differenceX.Add((calibrationInfo.MarkPoint.X - ox) - (matrix * pixel)[0]);
+                    differenceY.Add((calibrationInfo.MarkPoint.Y - oy) - (matrix * pixel)[1]);
+                }
                 else
                 {
                     //将tool 0下的坐标转换成Tool n下的坐标
@@ -1386,36 +1386,36 @@ namespace TeamAAS_VP.Services
                     differenceX.Add(cc[0] - calibrationInfo.MarkPoint.X);
                     differenceY.Add(cc[1] - calibrationInfo.MarkPoint.Y);
                 }
-                //else if (calibrationInfo.CameraMount == Enums.CameraMount.MobileDown_XYPlatform)
-                //{
-                //    //像素直接转换成mm时的点位
-                //    double Robot_X, Robot_Y;
-                //    Robot_X = TestPos[i][0];
-                //    Robot_Y = TestPos[i][1];
-
-                //    //机器人当前TOOL 0下的坐标
-                //    double curpos_x = 0, curpos_y = 0, curpos_u = 0;
-                //    curpos_x = rPoints[i].X;
-                //    curpos_y = rPoints[i].Y;
-                //    curpos_u = rPoints[i].U;
-
-                //    //得到旋转矩阵
-                //    double angle = Math.PI * (curpos_u - calibrationInfo.MarkPoint.U) / 180;
-                //    // 创建T矩阵
-                //    Matrix<double> rotationMatrix = Matrix<double>.Build.DenseOfArray(new double[,]
-                //    {
-                //                { Math.Cos(angle), -Math.Sin(angle) },
-                //                { Math.Sin(angle), Math.Cos(angle) }
-                //    });
-
-                //    Vector<double> p3 = Vector<double>.Build.Dense(new double[] { Robot_X - calibrationInfo.CalibPoints[0].Robot.X, Robot_Y - calibrationInfo.CalibPoints[0].Robot.Y });
-                //    Vector<double> phere = Vector<double>.Build.Dense(new double[] { curpos_x, curpos_y });
-
-                //    var cc = rotationMatrix * p3 + phere;
-
-                //    differenceX.Add(cc[0] - calibrationInfo.MarkPoint.X);
-                //    differenceY.Add(cc[1] - calibrationInfo.MarkPoint.Y);
-                //}
+                else if (calibrationInfo.CameraMount == Enums.CameraMount.MobileDown_XYPlatform)
+                {
+                    //像素直接转换成mm时的点位
+                    double Robot_X, Robot_Y;
+                    Robot_X = TestPos[i][0];
+                    Robot_Y = TestPos[i][1];
+
+                    //机器人当前TOOL 0下的坐标
+                    double curpos_x = 0, curpos_y = 0, curpos_u = 0;
+                    curpos_x = rPoints[i].X;
+                    curpos_y = rPoints[i].Y;
+                    curpos_u = rPoints[i].U;
+
+                    //得到旋转矩阵
+                    double angle = Math.PI * (curpos_u - calibrationInfo.MarkPoint.U) / 180;
+                    // 创建T矩阵
+                    Matrix<double> rotationMatrix = Matrix<double>.Build.DenseOfArray(new double[,]
+                    {
+                                { Math.Cos(angle), -Math.Sin(angle) },
+                                { Math.Sin(angle), Math.Cos(angle) }
+                    });
+
+                    Vector<double> p3 = Vector<double>.Build.Dense(new double[] { Robot_X - calibrationInfo.CalibPoints[0].Robot.X, Robot_Y - calibrationInfo.CalibPoints[0].Robot.Y });
+                    Vector<double> phere = Vector<double>.Build.Dense(new double[] { curpos_x, curpos_y });
+
+                    var cc = rotationMatrix * p3 + phere;
+
+                    differenceX.Add(cc[0] - calibrationInfo.MarkPoint.X);
+                    differenceY.Add(cc[1] - calibrationInfo.MarkPoint.Y);
+                }
                 else
                 {
                     //将tool 0下的坐标转换成Tool n下的坐标

+ 8 - 1
TeamAAS-VM/TeamAAS-VP.csproj

@@ -529,10 +529,10 @@
     <Compile Include="Controls\PlcRobotManual.xaml.cs">
       <DependentUpon>PlcRobotManual.xaml</DependentUpon>
     </Compile>
-    <Compile Include="Core\Robots\AxisViewModel.cs" />
     <Compile Include="Events\MainTabSwitchNotification.cs" />
     <Compile Include="Models\Feeder\ScrewFeederInfo.cs" />
     <Compile Include="Models\ScrewFeederBatchRecord.cs" />
+    <Compile Include="ViewModels\DebugMod\PlcRobotManualStepViewModel.cs" />
     <Compile Include="ViewModels\Product\ImageDisplayViewModel.cs" />
     <Compile Include="ViewModels\Setting\AddScrewFeederViewModel.cs" />
     <Compile Include="ViewModels\Statistics\LockResultRecoredQueryViewModel.cs" />
@@ -916,6 +916,9 @@
     <Compile Include="Views\DebugMod\LightManual.xaml.cs">
       <DependentUpon>LightManual.xaml</DependentUpon>
     </Compile>
+    <Compile Include="Views\DebugMod\PlcRobotManualStep.xaml.cs">
+      <DependentUpon>PlcRobotManualStep.xaml</DependentUpon>
+    </Compile>
     <Compile Include="Views\DebugMod\RobotManualStep.xaml.cs">
       <DependentUpon>RobotManualStep.xaml</DependentUpon>
     </Compile>
@@ -1222,6 +1225,10 @@
       <SubType>Designer</SubType>
       <Generator>MSBuild:Compile</Generator>
     </Page>
+    <Page Include="Views\DebugMod\PlcRobotManualStep.xaml">
+      <SubType>Designer</SubType>
+      <Generator>MSBuild:Compile</Generator>
+    </Page>
     <Page Include="Views\DebugMod\RobotManualStep.xaml">
       <SubType>Designer</SubType>
       <Generator>MSBuild:Compile</Generator>

+ 452 - 0
TeamAAS-VM/ViewModels/DebugMod/PlcRobotManualStepViewModel.cs

@@ -0,0 +1,452 @@
+using Prism.Commands;
+using Prism.Events;
+using Prism.Ioc;
+using Prism.Mvvm;
+using Prism.Regions;
+using Prism.Unity;
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Linq;
+using System.Windows;
+using TeamAAS_VP.Core.Robots;
+using TeamAAS_VP.Events;
+using TeamAAS_VP.Interfaces;
+using TeamAAS_VP.Resources.Languages;
+
+namespace TeamAAS_VP.ViewModels.DebugMod
+{
+    public class PlcRobotManualStepViewModel : BindableBase, IConfirmNavigationRequest
+    {
+        IContainerProvider _container;
+        IEventAggregator _eventAggregator;
+        IRobotService _robotService;
+        ISystemDatabaseService _systemDatabaseService;
+        IConfigService _configService;
+        public PlcRobotManualStepViewModel(IContainerProvider container, IEventAggregator ea, IRobotService robotService, ISystemDatabaseService systemDatabaseService, IConfigService configService)
+        {
+            _container = container;
+            _eventAggregator = ea;
+            _robotService = robotService;
+            _systemDatabaseService = systemDatabaseService;
+            _configService = configService;
+            //订阅权限登录事件
+            _eventAggregator.GetEvent<UserLoginNotification>().Subscribe(LoginChange);
+        }
+
+        #region 属性
+
+        private ObservableCollection<AxisExtended> _AdditionalAxisList;
+        public ObservableCollection<AxisExtended> AdditionalAxisList
+        {
+            get { return _AdditionalAxisList; }
+            set { SetProperty(ref _AdditionalAxisList, value); }
+        }
+
+        private XYZU_Robot _Robot;
+
+        public XYZU_Robot Robot
+        {
+            get { return _Robot; }
+            set
+            {
+                SetProperty(ref _Robot, value);
+                try
+                {
+                    if (value != null)
+                    {
+                        this.Distance = _configService.GetRobotStepDistance(value.Id);
+                    }
+                }
+                catch (Exception)
+                {
+
+                }
+            }
+        }
+
+        private double _Distance = 1;
+
+        public double Distance
+        {
+            get { return _Distance; }
+            set
+            {
+                SetProperty(ref _Distance, value);
+                try
+                {
+                    if (Robot == null)
+                    {
+                        return;
+                    }
+                    if (_configService == null)
+                        _configService = ((PrismApplication)(App.Current)).Container.Resolve<IConfigService>();
+                    _configService.UpdateRobotStepDistance(Robot.Id, value);
+                }
+                catch (Exception)
+                {
+
+                }
+            }
+        }
+        #endregion
+
+        #region 命令
+
+        private DelegateCommand<string> _MotorCommand;
+        public DelegateCommand<string> MotorCommand =>
+            _MotorCommand ?? (_MotorCommand = new DelegateCommand<string>(ExecuteMotorCommand).ObservesCanExecute(() => CanExecute).ObservesProperty(() => Robot).ObservesProperty(() => Robot.CanExecute));
+
+
+
+        private DelegateCommand _ResetCommand;
+        public DelegateCommand ResetCommand =>
+            _ResetCommand ?? (_ResetCommand = new DelegateCommand(ExecuteResetCommand).ObservesCanExecute(() => CanExecute).ObservesProperty(() => Robot).ObservesProperty(() => Robot.CanExecute));
+
+
+
+        private DelegateCommand<object> _JogCommand;
+        public DelegateCommand<object> JogCommand =>
+            _JogCommand ?? (_JogCommand = new DelegateCommand<object>(ExecuteJogCommand).ObservesCanExecute(() => CanExecute).ObservesProperty(() => Robot).ObservesProperty(() => Robot.CanExecute));
+
+        //附加轴,轴点动+命令
+        private DelegateCommand<object> _AdditionalAxisJogPlusCommand;
+        public DelegateCommand<object> AdditionalAxisJogPlusCommand =>
+            _AdditionalAxisJogPlusCommand ?? (_AdditionalAxisJogPlusCommand = new DelegateCommand<object>(ExecuteAdditionalAxisJogPlusCommand).ObservesCanExecute(() => CanExecute).ObservesProperty(() => Robot).ObservesProperty(() => Robot.CanExecute));
+
+        //附加轴,轴点动-命令
+        private DelegateCommand<object> _AdditionalAxisJogMinusCommand;
+        public DelegateCommand<object> AdditionalAxisJogMinusCommand =>
+            _AdditionalAxisJogMinusCommand ?? (_AdditionalAxisJogMinusCommand = new DelegateCommand<object>(ExecuteAdditionalAxisJogMinusCommand).ObservesCanExecute(() => CanExecute).ObservesProperty(() => Robot).ObservesProperty(() => Robot.CanExecute));
+
+        private DelegateCommand<object> _AdditionalAxisMotorOnCommand;
+        public DelegateCommand<object> AdditionalAxisMotorOnCommand =>
+            _AdditionalAxisMotorOnCommand ?? (_AdditionalAxisMotorOnCommand = new DelegateCommand<object>(ExecuteAdditionalAxisMotorOnCommand).ObservesCanExecute(() => CanExecute).ObservesProperty(() => Robot).ObservesProperty(() => Robot.CanExecute));
+
+        private DelegateCommand<object> _AdditionalAxisMotorOffCommand;
+        public DelegateCommand<object> AdditionalAxisMotorOffCommand =>
+            _AdditionalAxisMotorOffCommand ?? (_AdditionalAxisMotorOffCommand = new DelegateCommand<object>(ExecuteAdditionalAxisMotorOffCommand).ObservesCanExecute(() => CanExecute).ObservesProperty(() => Robot).ObservesProperty(() => Robot.CanExecute));
+
+
+        #endregion
+
+        #region 事件
+
+        #endregion
+
+        #region 方法
+
+        private void ShowMsg(string msg)
+        {
+            _eventAggregator.GetEvent<SnackbarMessageNotification>().Publish(new MessageParameter() { Msg = $"{Lang.成功}!", Duration = 0.4 });
+        }
+
+        /// <summary>
+        /// 电机
+        /// </summary>
+        /// <param name="obj"></param>
+        private async void ExecuteMotorCommand(string obj)
+        {
+            if (Robot == null || !Robot.IsConnected) return;
+            try
+            {
+                await Robot.MotorAsync(Convert.ToBoolean(obj));
+                ShowMsg($"{Lang.完成}!");
+            }
+            catch (Exception ex)
+            {
+                LogHelper.WriteLogError("机器人点击操作时出错!", ex);
+                ShowMsg(ex.Message);
+            }
+        }
+
+        /// <summary>
+        /// 重置
+        /// </summary>
+        private async void ExecuteResetCommand()
+        {
+            if (Robot == null || !Robot.IsConnected) return;
+            try
+            {
+                await Robot.ResetAsync();
+
+                ShowMsg($"{Lang.完成}!");
+            }
+            catch (Exception ex)
+            {
+                LogHelper.WriteLogError("重置机器人时出错!", ex);
+                ShowMsg(ex.Message);
+            }
+        }
+
+        /// <summary>
+        /// 点动
+        /// </summary>
+        /// <param name="obj"></param>
+        private async void ExecuteJogCommand(object obj)
+        {
+            if (Robot == null || !Robot.IsConnected) return;
+            try
+            {
+                var items = ((string)obj).Split(':');
+                //如果移动距离大于10mm,则提示用户确认
+                double distance = double.Parse(items[1]);
+                if (Math.Abs(distance) >= 10)
+                {
+                    if (MessageBox.Show($"确认要点动距离 {distance} mm 吗?", "确认点动", MessageBoxButton.YesNo, MessageBoxImage.Question) != MessageBoxResult.Yes)
+                    {
+                        return;
+                    }
+                }
+                Robot.Timeout = 6000000;
+                switch (items[0])
+                {
+                    case "X+":
+                        await Robot.JogAsync("X", distance);
+                        break;
+                    case "X-":
+                        await Robot.JogAsync("X", -distance);
+                        break;
+                    case "Y+":
+                        await Robot.JogAsync("Y", distance);
+                        break;
+                    case "Y-":
+                        await Robot.JogAsync("Y", -distance);
+                        break;
+                    case "Z+":
+                        await Robot.JogAsync("Z", distance);
+                        break;
+                    case "Z-":
+                        await Robot.JogAsync("Z", -distance);
+                        break;
+                    case "U+":
+                        await Robot.JogAsync("U", distance);
+                        break;
+                    case "U-":
+                        await Robot.JogAsync("U", -distance);
+                        break;
+                    case "V+":
+                        await Robot.JogAsync("V", distance);
+                        break;
+                    case "V-":
+                        await Robot.JogAsync("V", -distance);
+                        break;
+                    case "W+":
+                        await Robot.JogAsync("W", distance);
+                        break;
+                    case "W-":
+                        await Robot.JogAsync("W", -distance);
+                        break;
+                    default:
+                        break;
+                }
+                Robot.Timeout = 5000;
+
+
+                ShowMsg($"{Lang.完成}!");
+            }
+            catch (Exception ex)
+            {
+                LogHelper.WriteLogError("机器点动时出错!", ex);
+                ShowMsg(ex.Message);
+            }
+        }
+
+        private bool CanExecute
+        {
+            get
+            {
+                if (Robot != null)
+                {
+                    return Robot.CanExecute;
+                }
+                else
+                {
+                    return false;
+                }
+            }
+        }
+
+        /// <summary>
+        /// 附加轴+点动
+        /// </summary>
+        /// <param name="obj"></param>
+        async void ExecuteAdditionalAxisJogPlusCommand(object obj)
+        {
+            if (Robot == null || !Robot.IsConnected) return;
+            try
+            {
+                if (obj is AxisExtended axis)
+                {
+                    //如果移动距离大于10mm,则提示用户确认
+                    if (Math.Abs(Distance) >= 10)
+                    {
+                        if (MessageBox.Show($"确认要点动距离 {Distance} mm 吗?", "确认点动", MessageBoxButton.YesNo, MessageBoxImage.Question) != MessageBoxResult.Yes)
+                        {
+                            return;
+                        }
+                    }
+                    await Robot.AdditionalAxisJogAsync(axis.Name, Distance);
+                    ShowMsg($"{Lang.完成}!");
+                }
+            }
+            catch (Exception ex)
+            {
+                LogHelper.WriteLogError("附加轴点动+时出错!", ex);
+                ShowMsg(ex.Message);
+            }
+        }
+
+        /// <summary>
+        /// 附加轴-点动
+        /// </summary>
+        /// <param name="obj"></param>
+        async void ExecuteAdditionalAxisJogMinusCommand(object obj)
+        {
+            if (Robot == null || !Robot.IsConnected) return;
+            try
+            {
+                if (obj is AxisExtended axis)
+                {
+                    //如果移动距离大于10mm,则提示用户确认
+                    if (Math.Abs(Distance) >= 10)
+                    {
+                        if (MessageBox.Show($"确认要点动距离 {Distance} mm 吗?", "确认点动", MessageBoxButton.YesNo, MessageBoxImage.Question) != MessageBoxResult.Yes)
+                        {
+                            return;
+                        }
+                    }
+                    await Robot.AdditionalAxisJogNegAsync(axis.Name, Distance);
+                    ShowMsg($"{Lang.完成}!");
+                }
+            }
+            catch (Exception ex)
+            {
+                LogHelper.WriteLogError("附加轴点动-时出错!", ex);
+                ShowMsg(ex.Message);
+            }
+        }
+
+        /// <summary>
+        /// 附加轴上使能
+        /// </summary>
+        /// <param name="obj"></param>
+        async void ExecuteAdditionalAxisMotorOffCommand(object obj)
+        {
+            if (Robot == null || !Robot.IsConnected) return;
+            try
+            {
+                if (obj is AxisExtended axis)
+                {
+                    await Robot.AdditionalAxisMotorAsync(axis.Name, true);
+                    ShowMsg($"{Lang.完成}!");
+                }
+            }
+            catch (Exception ex)
+            {
+                LogHelper.WriteLogError("附加轴上使能时出错!", ex);
+                ShowMsg(ex.Message);
+            }
+        }
+
+        /// <summary>
+        /// 附加轴下使能
+        /// </summary>
+        /// <param name="obj"></param>
+        async void ExecuteAdditionalAxisMotorOnCommand(object obj)
+        {
+            if (Robot == null || !Robot.IsConnected) return;
+            try
+            {
+                if (obj is AxisExtended axis)
+                {
+                    await Robot.AdditionalAxisMotorAsync(axis.Name, true);
+                    ShowMsg($"{Lang.完成}!");
+                }
+            }
+            catch (Exception ex)
+            {
+                LogHelper.WriteLogError("附加轴下使能时出错!", ex);
+                ShowMsg(ex.Message);
+            }
+        }
+
+        #endregion
+
+        #region 继承
+        /// <summary>
+        /// 确认导航请求时调用。此方法允许您在导航之前执行一些操作。
+        /// </summary>
+        /// <param name="navigationContext"></param>
+        /// <param name="continuationCallback"></param>
+        public void ConfirmNavigationRequest(NavigationContext navigationContext, Action<bool> continuationCallback)
+        {
+            continuationCallback(true);
+        }
+
+        /// <summary>
+        /// 接收导航请求时调用。传入导航参数,包含有关导航目标的信息。
+        /// </summary>
+        /// <param name="navigationContext"></param>
+        /// <exception cref="NotImplementedException"></exception>
+        public async void OnNavigatedTo(NavigationContext navigationContext)
+        {
+            var robotInfo = navigationContext.Parameters["RobotInfo"] as Models.RobotInfo;
+            if (robotInfo != null)
+            {
+                Robot = _robotService.GetRobot(robotInfo.Id) as XYZU_Robot;
+                Robot.EnterDebugMode = true;
+            }
+
+            if (_systemDatabaseService.GetCurrentUser().userPart == Enums.UserPart.Operator)
+            {
+                IsAllowEdit = false;
+            }
+            else
+            {
+                IsAllowEdit = true;
+            }
+        }
+
+        /// <summary>
+        /// 是否允许导航到此视图模型。
+        /// </summary>
+        /// <param name="navigationContext"></param>
+        /// <returns></returns>
+        public bool IsNavigationTarget(NavigationContext navigationContext)
+        {
+            return true;
+        }
+
+        /// <summary>
+        /// 导航离开此视图模型时调用。您可以在此处执行清理操作或保存状态。
+        /// </summary>
+        /// <param name="navigationContext"></param>
+        public void OnNavigatedFrom(NavigationContext navigationContext)
+        {
+            if (Robot != null)
+                Robot.EnterDebugMode = false;
+        }
+        #endregion
+
+        private bool _IsAllowEdit = false;
+
+        public bool IsAllowEdit
+        {
+            get { return _IsAllowEdit; }
+            set { SetProperty(ref _IsAllowEdit, value); }
+        }
+
+        private void LoginChange(Models.User user)
+        {
+            if (user.userPart == Enums.UserPart.Operator)
+            {
+                IsAllowEdit = false;
+            }
+            else
+            {
+                IsAllowEdit = true;
+            }
+        }
+    }
+}

+ 5 - 1
TeamAAS-VM/ViewModels/DebugViewModel.cs

@@ -12,6 +12,7 @@ using System.Linq;
 using System.Windows.Controls;
 using Team.FFFeederService;
 using TeamAAS_VP.Core;
+using TeamAAS_VP.Enums;
 using TeamAAS_VP.Events;
 using TeamAAS_VP.Interfaces;
 using TeamAAS_VP.Models;
@@ -133,7 +134,10 @@ namespace TeamAAS_VP.ViewModels
                     SelectRobot = e.NewValue as RobotInfo;
                     NavigationParameters keyValues = new NavigationParameters();
                     keyValues.Add("RobotInfo", SelectRobot);
-                    _regionManager.RequestNavigate("DebugRegionContext", "RobotManualStep", keyValues);
+                    if (SelectRobot != null && SelectRobot.RobotBrand == RobotBrand.XYZ_Platform || SelectRobot.RobotBrand == RobotBrand.XYZU_Platform)
+                        _regionManager.RequestNavigate("DebugRegionContext", "PlcRobotManualStep", keyValues);
+                    else
+                        _regionManager.RequestNavigate("DebugRegionContext", "RobotManualStep", keyValues);
                 }
                 else if (e.NewValue is FeederInfo)
                 {

+ 134 - 4
TeamAAS-VM/ViewModels/Product/PlcPointParamsViewModel.cs

@@ -19,6 +19,7 @@ using System.Windows;
 using TeamAAS_VP.Controls;
 using TeamAAS_VP.Core;
 using TeamAAS_VP.Core.PLCs;
+using TeamAAS_VP.Core.Robots;
 using TeamAAS_VP.DxfModule;
 using TeamAAS_VP.Enums;
 using TeamAAS_VP.Events;
@@ -49,6 +50,13 @@ namespace TeamAAS_VP.ViewModels.Product
 
         #region 属性
 
+        private ObservableCollection<AxisExtended> _AdditionalAxisList;
+        public ObservableCollection<AxisExtended> AdditionalAxisList
+        {
+            get { return _AdditionalAxisList; }
+            set { SetProperty(ref _AdditionalAxisList, value); }
+        }
+
         private ProductModel _SelectProduct;
 
         /// <summary>
@@ -127,9 +135,9 @@ namespace TeamAAS_VP.ViewModels.Product
 
         public Management management { get; set; }
 
-        private IRobot _Robot;
+        private XYZU_Robot _Robot;
 
-        public IRobot Robot
+        public XYZU_Robot Robot
         {
             get { return _Robot; }
             set
@@ -443,7 +451,25 @@ namespace TeamAAS_VP.ViewModels.Product
         private DelegateCommand _StopGrabbingCommand;
         public DelegateCommand StopGrabbingCommand =>
             _StopGrabbingCommand ?? (_StopGrabbingCommand = new DelegateCommand(ExecuteStopGrabbingCommand, CanExecuteStopGrabbingCommand).ObservesProperty(() => IsGrap).ObservesProperty(() => SelectedCamera));
-        
+
+        //附加轴,轴点动+命令
+        private DelegateCommand<object> _AdditionalAxisJogPlusCommand;
+        public DelegateCommand<object> AdditionalAxisJogPlusCommand =>
+            _AdditionalAxisJogPlusCommand ?? (_AdditionalAxisJogPlusCommand = new DelegateCommand<object>(ExecuteAdditionalAxisJogPlusCommand).ObservesCanExecute(() => CanExecute).ObservesProperty(() => Robot).ObservesProperty(() => Robot.CanExecute));
+
+        //附加轴,轴点动-命令
+        private DelegateCommand<object> _AdditionalAxisJogMinusCommand;
+        public DelegateCommand<object> AdditionalAxisJogMinusCommand =>
+            _AdditionalAxisJogMinusCommand ?? (_AdditionalAxisJogMinusCommand = new DelegateCommand<object>(ExecuteAdditionalAxisJogMinusCommand).ObservesCanExecute(() => CanExecute).ObservesProperty(() => Robot).ObservesProperty(() => Robot.CanExecute));
+
+        private DelegateCommand<object> _AdditionalAxisMotorOnCommand;
+        public DelegateCommand<object> AdditionalAxisMotorOnCommand =>
+            _AdditionalAxisMotorOnCommand ?? (_AdditionalAxisMotorOnCommand = new DelegateCommand<object>(ExecuteAdditionalAxisMotorOnCommand).ObservesCanExecute(() => CanExecute).ObservesProperty(() => Robot).ObservesProperty(() => Robot.CanExecute));
+
+        private DelegateCommand<object> _AdditionalAxisMotorOffCommand;
+        public DelegateCommand<object> AdditionalAxisMotorOffCommand =>
+            _AdditionalAxisMotorOffCommand ?? (_AdditionalAxisMotorOffCommand = new DelegateCommand<object>(ExecuteAdditionalAxisMotorOffCommand).ObservesCanExecute(() => CanExecute).ObservesProperty(() => Robot).ObservesProperty(() => Robot.CanExecute));
+
         #endregion
 
         #region 事件
@@ -1549,6 +1575,110 @@ namespace TeamAAS_VP.ViewModels.Product
                      }
                  }));
         }
+
+        /// <summary>
+        /// 附加轴+点动
+        /// </summary>
+        /// <param name="obj"></param>
+        async void ExecuteAdditionalAxisJogPlusCommand(object obj)
+        {
+            if (Robot == null || !Robot.IsConnected) return;
+            try
+            {
+                if (obj is AxisExtended axis)
+                {
+                    //如果移动距离大于10mm,则提示用户确认
+                    if (Math.Abs(Distance) >= 10)
+                    {
+                        if (MessageBox.Show($"确认要点动距离 {Distance} mm 吗?", "确认点动", MessageBoxButton.YesNo, MessageBoxImage.Question) != MessageBoxResult.Yes)
+                        {
+                            return;
+                        }
+                    }
+                    await Robot.AdditionalAxisJogAsync(axis.Name, Distance);
+                    _eventAggregator.GetEvent<SnackbarMessageNotification>().Publish(new MessageParameter() { Msg = $"{Lang.完成}!", Duration = 0.4 });
+                }
+            }
+            catch (Exception ex)
+            {
+                LogHelper.WriteLogError("附加轴点动+时出错!", ex);
+                _eventAggregator.GetEvent<SnackbarMessageNotification>().Publish(new MessageParameter() { Msg =ex.Message, Duration = 0.4 });
+            }
+        }
+
+        /// <summary>
+        /// 附加轴-点动
+        /// </summary>
+        /// <param name="obj"></param>
+        async void ExecuteAdditionalAxisJogMinusCommand(object obj)
+        {
+            if (Robot == null || !Robot.IsConnected) return;
+            try
+            {
+                if (obj is AxisExtended axis)
+                {
+                    //如果移动距离大于10mm,则提示用户确认
+                    if (Math.Abs(Distance) >= 10)
+                    {
+                        if (MessageBox.Show($"确认要点动距离 {Distance} mm 吗?", "确认点动", MessageBoxButton.YesNo, MessageBoxImage.Question) != MessageBoxResult.Yes)
+                        {
+                            return;
+                        }
+                    }
+                    await Robot.AdditionalAxisJogNegAsync(axis.Name, Distance);
+                    _eventAggregator.GetEvent<SnackbarMessageNotification>().Publish(new MessageParameter() { Msg = $"{Lang.完成}!", Duration = 0.4 });
+                }
+            }
+            catch (Exception ex)
+            {
+                LogHelper.WriteLogError("附加轴点动-时出错!", ex);
+                _eventAggregator.GetEvent<SnackbarMessageNotification>().Publish(new MessageParameter() { Msg = ex.Message, Duration = 0.4 });
+            }
+        }
+
+        /// <summary>
+        /// 附加轴上使能
+        /// </summary>
+        /// <param name="obj"></param>
+        async void ExecuteAdditionalAxisMotorOffCommand(object obj)
+        {
+            if (Robot == null || !Robot.IsConnected) return;
+            try
+            {
+                if (obj is AxisExtended axis)
+                {
+                    await Robot.AdditionalAxisMotorAsync(axis.Name, true);
+                    _eventAggregator.GetEvent<SnackbarMessageNotification>().Publish(new MessageParameter() { Msg = $"{Lang.完成}!", Duration = 0.4 });
+                }
+            }
+            catch (Exception ex)
+            {
+                LogHelper.WriteLogError("附加轴上使能时出错!", ex);
+                _eventAggregator.GetEvent<SnackbarMessageNotification>().Publish(new MessageParameter() { Msg = ex.Message, Duration = 0.4 });
+            }
+        }
+
+        /// <summary>
+        /// 附加轴下使能
+        /// </summary>
+        /// <param name="obj"></param>
+        async void ExecuteAdditionalAxisMotorOnCommand(object obj)
+        {
+            if (Robot == null || !Robot.IsConnected) return;
+            try
+            {
+                if (obj is AxisExtended axis)
+                {
+                    await Robot.AdditionalAxisMotorAsync(axis.Name, true);
+                    _eventAggregator.GetEvent<SnackbarMessageNotification>().Publish(new MessageParameter() { Msg = $"{Lang.完成}!", Duration = 0.4 });
+                }
+            }
+            catch (Exception ex)
+            {
+                LogHelper.WriteLogError("附加轴下使能时出错!", ex);
+                _eventAggregator.GetEvent<SnackbarMessageNotification>().Publish(new MessageParameter() { Msg = ex.Message, Duration = 0.4 });
+            }
+        }
         #endregion
 
         #region 继承
@@ -1580,7 +1710,7 @@ namespace TeamAAS_VP.ViewModels.Product
 
                 if (Robot == null)
                 {
-                    Robot = _robotService.GetRobotByNumber(1);
+                    Robot = _robotService.GetRobotByNumber(1) as XYZU_Robot;
                 }
 
                 if (Robot != null && Robot.IsConnected)

+ 330 - 0
TeamAAS-VM/Views/DebugMod/PlcRobotManualStep.xaml

@@ -0,0 +1,330 @@
+<UserControl x:Class="TeamAAS_VP.Views.DebugMod.PlcRobotManualStep"
+             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+             xmlns:prism="http://prismlibrary.com/"
+             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+             xmlns:vm="clr-namespace:TeamAAS_VP.ViewModels.DebugMod"
+             xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
+             xmlns:mah="http://metro.mahapps.com/winfx/xaml/controls"
+             xmlns:b="http://schemas.microsoft.com/xaml/behaviors"
+             xmlns:sys="clr-namespace:System;assembly=mscorlib"
+             xmlns:uControl="clr-namespace:TeamAAS_VP.Controls"
+             xmlns:convert="clr-namespace:TeamAAS_VP.ValueConverter"
+             xmlns:lex="http://wpflocalizeextension.codeplex.com"
+             lex:LocalizeDictionary.DesignCulture="zh-CN"
+             lex:ResxLocalizationProvider.DefaultAssembly="TeamAAS-VP"
+             lex:ResxLocalizationProvider.DefaultDictionary="Lang"
+             prism:ViewModelLocator.AutoWireViewModel="True"
+             mc:Ignorable="d"
+             d:Background="White"
+             d:DataContext="{d:DesignInstance Type=vm:PlcRobotManualStepViewModel}"
+             FontFamily="{DynamicResource DefaultFont}">
+    <UserControl.Resources>
+        <convert:JogConverter x:Key="JogConverter" />
+    </UserControl.Resources>
+    <ScrollViewer HorizontalAlignment="Stretch"
+                  VerticalAlignment="Stretch">
+        <Grid HorizontalAlignment="Stretch"
+              VerticalAlignment="Top"
+              IsEnabled="{Binding IsAllowEdit}">
+            <Grid.RowDefinitions>
+                <RowDefinition Height="auto" />
+                <RowDefinition Height="auto" />
+            </Grid.RowDefinitions>
+
+            <Grid Grid.Row="0"
+                  Margin="10"
+                  HorizontalAlignment="Center">
+                <Grid.ColumnDefinitions>
+                    <ColumnDefinition Width="auto" />
+                    <ColumnDefinition Width="auto" />
+                    <ColumnDefinition Width="auto" />
+                    <ColumnDefinition Width="auto" />
+                </Grid.ColumnDefinitions>
+                <Grid.RowDefinitions>
+                    <RowDefinition Height="auto" />
+                    <RowDefinition Height="auto" />
+                    <RowDefinition Height="auto" />
+                    <RowDefinition Height="auto" />
+                    <RowDefinition Height="auto" />
+                    <RowDefinition Height="auto" />
+                    <RowDefinition Height="auto" />
+                </Grid.RowDefinitions>
+                <Grid.Resources>
+                    <Style TargetType="Button"
+                           BasedOn="{StaticResource MaterialDesignRaisedDarkButton}">
+                        <Setter Property="Width"
+                                Value="60" />
+                        <Setter Property="Height"
+                                Value="60" />
+                        <Setter Property="FontSize"
+                                Value="13" />
+                        <Setter Property="Padding"
+                                Value="0" />
+                        <Setter Property="HorizontalAlignment"
+                                Value="Center" />
+                        <Setter Property="VerticalAlignment"
+                                Value="Center" />
+                        <Setter Property="Margin"
+                                Value="2" />
+                        <Setter Property="materialDesign:ButtonAssist.CornerRadius"
+                                Value="10" />
+                    </Style>
+                </Grid.Resources>
+                <StackPanel Grid.ColumnSpan="4"
+                            Grid.Row="0"
+                            Grid.Column="0"
+                            VerticalAlignment="Center"
+                            Orientation="Horizontal">
+                    <Button Content="{lex:Loc 上使能}"
+                            Grid.Row="2"
+                            materialDesign:ButtonAssist.CornerRadius="10"
+                            Style="{StaticResource MaterialDesignRaisedDarkButton}"
+                            Command="{Binding MotorCommand}"
+                            CommandParameter="True" />
+                    <Button Content="{lex:Loc 下使能}"
+                            Grid.Row="3"
+                            Margin="10"
+                            materialDesign:ButtonAssist.CornerRadius="10"
+                            Style="{StaticResource MaterialDesignRaisedDarkButton}"
+                            Command="{Binding MotorCommand}"
+                            CommandParameter="False" />
+                    <Button Content="{lex:Loc 复位错误}"
+                            Grid.Row="4"
+                            materialDesign:ButtonAssist.CornerRadius="10"
+                            Style="{StaticResource MaterialDesignRaisedDarkButton}"
+                            Command="{Binding ResetCommand}" />
+                </StackPanel>
+                <Button Grid.Row="2"
+                        Grid.Column="0"
+                        Command="{Binding JogCommand}"
+                        CommandParameter="{Binding ElementName=txtDistance,Path=Value,Converter={StaticResource JogConverter}, ConverterParameter=X-}">
+                    <StackPanel Orientation="Vertical">
+                        <materialDesign:PackIcon Kind="ArrowLeftBold"
+                                                 Width="26"
+                                                 Height="26" />
+                        <TextBlock Text="-X"
+                                   HorizontalAlignment="Center" />
+                    </StackPanel>
+                </Button>
+                <Button Grid.Row="2"
+                        Grid.Column="2"
+                        Command="{Binding JogCommand}"
+                        CommandParameter="{Binding ElementName=txtDistance,Path=Value,Converter={StaticResource JogConverter}, ConverterParameter=X+}">
+                    <StackPanel Orientation="Vertical">
+                        <materialDesign:PackIcon Kind="ArrowRightBold"
+                                                 Width="26"
+                                                 Height="26" />
+                        <TextBlock Text="+X"
+                                   HorizontalAlignment="Center" />
+                    </StackPanel>
+                </Button>
+                <Button Grid.Row="1"
+                        Grid.Column="1"
+                        Command="{Binding JogCommand}"
+                        CommandParameter="{Binding ElementName=txtDistance,Path=Value,Converter={StaticResource JogConverter}, ConverterParameter=Y+}">
+                    <StackPanel Orientation="Vertical">
+                        <materialDesign:PackIcon Kind="ArrowTopBold"
+                                                 Width="26"
+                                                 Height="26" />
+                        <TextBlock Text="+Y"
+                                   HorizontalAlignment="Center" />
+                    </StackPanel>
+                </Button>
+                <Button Grid.Row="3"
+                        Grid.Column="1"
+                        Command="{Binding JogCommand}"
+                        CommandParameter="{Binding ElementName=txtDistance,Path=Value,Converter={StaticResource JogConverter}, ConverterParameter=Y-}">
+                    <StackPanel Orientation="Vertical">
+                        <materialDesign:PackIcon Kind="ArrowBottomBold"
+                                                 Width="26"
+                                                 Height="26" />
+                        <TextBlock Text="-Y"
+                                   HorizontalAlignment="Center" />
+                    </StackPanel>
+                </Button>
+                <Button Grid.Row="1"
+                        Grid.Column="3"
+                        Command="{Binding JogCommand}"
+                        CommandParameter="{Binding ElementName=txtDistance,Path=Value,Converter={StaticResource JogConverter}, ConverterParameter=Z+}">
+                    <StackPanel Orientation="Vertical">
+                        <materialDesign:PackIcon Kind="ArrowTopBold"
+                                                 Width="26"
+                                                 Height="26" />
+                        <TextBlock Text="+Z"
+                                   HorizontalAlignment="Center" />
+                    </StackPanel>
+                </Button>
+                <Button Grid.Row="3"
+                        Grid.Column="3"
+                        Command="{Binding JogCommand}"
+                        CommandParameter="{Binding ElementName=txtDistance,Path=Value,Converter={StaticResource JogConverter}, ConverterParameter=Z-}">
+                    <StackPanel Orientation="Vertical">
+                        <materialDesign:PackIcon Kind="ArrowBottomBold"
+                                                 Width="26"
+                                                 Height="26" />
+                        <TextBlock Text="-Z"
+                                   HorizontalAlignment="Center" />
+                    </StackPanel>
+                </Button>
+                <Button Grid.Row="4"
+                        Grid.Column="3"
+                        Command="{Binding JogCommand}"
+                        CommandParameter="{Binding ElementName=txtDistance,Path=Value,Converter={StaticResource JogConverter}, ConverterParameter=U+}">
+                    <StackPanel Orientation="Vertical">
+                        <materialDesign:PackIcon Kind="AxisZRotateCounterclockwise"
+                                                 Width="26"
+                                                 Height="26" />
+                        <TextBlock Text="+U"
+                                   HorizontalAlignment="Center" />
+                    </StackPanel>
+                </Button>
+                <Button Grid.Row="4"
+                        Grid.Column="0"
+                        Command="{Binding JogCommand}"
+                        CommandParameter="{Binding ElementName=txtDistance,Path=Value,Converter={StaticResource JogConverter}, ConverterParameter=U-}">
+                    <StackPanel Orientation="Vertical">
+                        <materialDesign:PackIcon Kind="AxisZRotateClockwise"
+                                                 Width="26"
+                                                 Height="26" />
+                        <TextBlock Text="-U"
+                                   HorizontalAlignment="Center" />
+                    </StackPanel>
+                </Button>
+                <!--<Button Grid.Row="4"
+                    Grid.Column="1"
+                    Grid.ColumnSpan="2"
+                    Command="{Binding JogCommand}"
+                    CommandParameter="{Binding ElementName=txtDistance,Path=Value,Converter={StaticResource JogConverter}, ConverterParameter=V+}">
+                <StackPanel Orientation="Vertical">
+                    <materialDesign:PackIcon Kind="AxisYRotateCounterclockwise"
+                                             Width="26"
+                                             Height="26" />
+                    <TextBlock Text="+V"
+                               HorizontalAlignment="Center" />
+                </StackPanel>
+            </Button>
+            <Button Grid.Row="5"
+                    Grid.Column="1"
+                    Grid.ColumnSpan="2"
+                    Command="{Binding JogCommand}"
+                    CommandParameter="{Binding ElementName=txtDistance,Path=Value,Converter={StaticResource JogConverter}, ConverterParameter=V-}">
+                <StackPanel Orientation="Vertical">
+                    <materialDesign:PackIcon Kind="AxisYRotateClockwise"
+                                             Width="26"
+                                             Height="26" />
+                    <TextBlock Text="-V"
+                               HorizontalAlignment="Center" />
+                </StackPanel>
+            </Button>
+            <Button Grid.Row="4"
+                    Grid.Column="3"
+                    Command="{Binding JogCommand}"
+                    CommandParameter="{Binding ElementName=txtDistance,Path=Value,Converter={StaticResource JogConverter}, ConverterParameter=W+}">
+                <StackPanel Orientation="Vertical">
+                    <materialDesign:PackIcon Kind="AxisXRotateCounterclockwise"
+                                             Width="26"
+                                             Height="26" />
+                    <TextBlock Text="+W"
+                               HorizontalAlignment="Center" />
+                </StackPanel>
+            </Button>
+            <Button Grid.Row="5"
+                    Grid.Column="3"
+                    Command="{Binding JogCommand}"
+                    CommandParameter="{Binding ElementName=txtDistance,Path=Value,Converter={StaticResource JogConverter}, ConverterParameter=W-}">
+                <StackPanel Orientation="Vertical">
+                    <materialDesign:PackIcon Kind="AxisXRotateClockwise"
+                                             Width="26"
+                                             Height="26" />
+                    <TextBlock Text="-W"
+                               HorizontalAlignment="Center" />
+                </StackPanel>
+            </Button>-->
+                <StackPanel Grid.Row="6"
+                            Grid.ColumnSpan="7"
+                            Margin="10,10,0,10"
+                            Grid.Column="0"
+                            VerticalAlignment="Center"
+                            Orientation="Horizontal">
+                    <TextBlock Text="{lex:Loc 步进距离}"
+                               VerticalAlignment="Center" />
+                    <uControl:myNumericUpDowm x:Name="txtDistance"
+                                              MinWidth="100"
+                                              Interval="0.1"
+                                              Minimum="0"
+                                              Maximum="100"
+                                              StringFormat="{}{0:N3} mm"
+                                              Value="{Binding Distance,Mode=TwoWay}"
+                                              Margin="10,0" />
+                </StackPanel>
+            </Grid>
+
+            <!-- 附加轴列表:绑定到视图模型中的 ExtraAxes 集合(每项假定有 Name、Position) -->
+            <ItemsControl Grid.Row="1"
+                          ItemsSource="{Binding AdditionalAxisList}">
+                <ItemsControl.ItemTemplate>
+                    <DataTemplate>
+                        <Border Padding="6"
+                                Margin="0,2"
+                                Background="{DynamicResource MaterialDesignPaper}">
+                            <DockPanel LastChildFill="False"
+                                       VerticalAlignment="Center">
+                                <StackPanel Orientation="Horizontal"
+                                            DockPanel.Dock="Left"
+                                            VerticalAlignment="Center">
+                                    <TextBlock Text="{Binding Name}"
+                                               Width="140"
+                                               VerticalAlignment="Center" />
+                                    <TextBlock Text="{Binding CurrentPosition, StringFormat='{}{0:F3} mm'}"
+                                               Width="140"
+                                               Margin="10,0"
+                                               VerticalAlignment="Center" />
+                                </StackPanel>
+                                <StackPanel Orientation="Horizontal"
+                                            HorizontalAlignment="Right"
+                                            DockPanel.Dock="Right">
+                                    <Button Content="-"
+                                            Width="60"
+                                            Height="36"
+                                            Margin="6,0"
+                                            Style="{StaticResource MaterialDesignRaisedDarkButton}"
+                                            Command="{Binding DataContext.AdditionalAxisJogPlusCommand, RelativeSource={RelativeSource AncestorType=UserControl}}"
+                                            CommandParameter="{Binding}" />
+                                    <Button Content="+"
+                                            Width="60"
+                                            Height="36"
+                                            Margin="6,0"
+                                            Style="{StaticResource MaterialDesignRaisedDarkButton}"
+                                            Command="{Binding DataContext.AdditionalAxisJogMinusCommand, RelativeSource={RelativeSource AncestorType=UserControl}}"
+                                            CommandParameter="{Binding}" />
+                                    <Button Content="上使能"
+                                            Width="60"
+                                            Height="36"
+                                            Margin="6,0"
+                                            Style="{StaticResource MaterialDesignRaisedDarkButton}"
+                                            Command="{Binding DataContext.AdditionalAxisMotorOnCommand, RelativeSource={RelativeSource AncestorType=UserControl}}"
+                                            CommandParameter="{Binding}" />
+                                    <Button Content="下使能"
+                                            Width="60"
+                                            Height="36"
+                                            Margin="6,0"
+                                            Style="{StaticResource MaterialDesignRaisedDarkButton}"
+                                            Command="{Binding DataContext.AdditionalAxisMotorOffCommand, RelativeSource={RelativeSource AncestorType=UserControl}}"
+                                            CommandParameter="{Binding}" />
+                                </StackPanel>
+                            </DockPanel>
+                        </Border>
+                    </DataTemplate>
+                </ItemsControl.ItemTemplate>
+            </ItemsControl>
+
+            <materialDesign:Snackbar Grid.RowSpan="2"
+                                     x:Name="snackbar"
+                                     Opacity="0.9"
+                                     HorizontalContentAlignment="Center"
+                                     VerticalAlignment="Top" />
+        </Grid>
+    </ScrollViewer>
+</UserControl>

+ 15 - 0
TeamAAS-VM/Views/DebugMod/PlcRobotManualStep.xaml.cs

@@ -0,0 +1,15 @@
+using System.Windows.Controls;
+
+namespace TeamAAS_VP.Views.DebugMod
+{
+    /// <summary>
+    /// Interaction logic for PlcRobotManualStep
+    /// </summary>
+    public partial class PlcRobotManualStep : UserControl
+    {
+        public PlcRobotManualStep()
+        {
+            InitializeComponent();
+        }
+    }
+}

+ 1 - 1
TeamAAS-VM/Views/Product/CameraParams.xaml

@@ -77,7 +77,7 @@
                                    d:IsRightDrawerOpen="true"
                                    IsEnabled="{Binding IsAllowEdit}">
             <materialDesign:DrawerHost.RightDrawerContent>
-                <uControl:RobotManual x:Name="robotManual"
+                <uControl:PlcRobotManual x:Name="robotManual"
                                       Visibility="{Binding IsHaveRobot,Converter={StaticResource BooleanToVisibilityConverter}}" />
             </materialDesign:DrawerHost.RightDrawerContent>
 

+ 2 - 1
TeamAAS-VM/Views/Product/CameraParams.xaml.cs

@@ -1,4 +1,5 @@
 using System.Windows.Controls;
+using TeamAAS_VP.Core.Robots;
 using TeamAAS_VP.Interfaces;
 using TeamAAS_VP.ViewModels.Product;
 
@@ -26,7 +27,7 @@ namespace TeamAAS_VP.Views.Product
         {
             if (e.PropertyName=="Robot")
             {
-                this.robotManual.Robot= VM.Robot;
+                this.robotManual.Robot= VM.Robot as XYZU_Robot;
             }
             else if (e.PropertyName == "Image")
             {

+ 312 - 308
TeamAAS-VM/Views/Product/PlcPointParams.xaml

@@ -1271,11 +1271,14 @@
 
                     <!--运动控制区-->
                     <Viewbox Grid.Column="1">
-                        <Grid Grid.Row="1">
-                            <Grid.ColumnDefinitions>
-                                <ColumnDefinition Width="auto" />
-                                <ColumnDefinition Width="auto" />
-                            </Grid.ColumnDefinitions>
+                        <Grid >
+                            <Grid.RowDefinitions>
+                                <RowDefinition Height="auto" />
+                                <RowDefinition Height="auto" />
+                                <RowDefinition Height="auto" />
+                                <RowDefinition Height="auto" />
+                            </Grid.RowDefinitions>
+
                             <ToggleButton Grid.ColumnSpan="2"
                                           HorizontalAlignment="Left"
                                           VerticalAlignment="Top"
@@ -1285,54 +1288,191 @@
                                           ToolTip="相机图像"
                                           Margin="5,0"
                                           IsChecked="{Binding IsLeftDrawerOpen,ElementName=DrawerHost}" />
-                            <Grid Grid.Column="0"
-                                  VerticalAlignment="Center">
+
+                            <Grid Grid.Row="0"
+                                  Margin="10"
+                                  HorizontalAlignment="Center">
+                                <Grid.ColumnDefinitions>
+                                    <ColumnDefinition Width="auto" />
+                                    <ColumnDefinition Width="auto" />
+                                    <ColumnDefinition Width="auto" />
+                                    <ColumnDefinition Width="auto" />
+                                </Grid.ColumnDefinitions>
                                 <Grid.RowDefinitions>
                                     <RowDefinition Height="auto" />
                                     <RowDefinition Height="auto" />
                                     <RowDefinition Height="auto" />
                                     <RowDefinition Height="auto" />
                                     <RowDefinition Height="auto" />
-                                </Grid.RowDefinitions>
-                                
-                                
-                                <Button Content="{lex:Loc 上使能}"
-                                        Grid.Row="0"
-                                        materialDesign:ButtonAssist.CornerRadius="10"
-                                        Style="{StaticResource MaterialDesignRaisedDarkButton}"
-                                        Command="{Binding DataContext.MotorCommand,RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}"
-                                        CommandParameter="True" />
-                                <Button Content="{lex:Loc 下使能}"
-                                        Grid.Row="1"
-                                        Margin="0,10,0,10"
-                                        materialDesign:ButtonAssist.CornerRadius="10"
-                                        Style="{StaticResource MaterialDesignRaisedDarkButton}"
-                                        Command="{Binding DataContext.MotorCommand,RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}"
-                                        CommandParameter="False" />
-                                <Button Content="{lex:Loc 复位错误}"
-                                        Grid.Row="2"
-                                        materialDesign:ButtonAssist.CornerRadius="10"
-                                        Style="{StaticResource MaterialDesignRaisedDarkButton}"
-                                        Command="{Binding DataContext.ResetCommand,RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}" />
-                                <Button Content="{lex:Loc 释放刹车}"
-                                        Grid.Row="3"
-                                        Margin="0,10,0,10"
-                                        materialDesign:ButtonAssist.CornerRadius="10"
-                                        Style="{StaticResource MaterialDesignRaisedDarkButton}"
-                                        Command="{Binding DataContext.sFreeCommand,RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}" />
-                                <Button Content="{lex:Loc 锁定刹车}"
-                                        Grid.Row="4"
-                                        materialDesign:ButtonAssist.CornerRadius="10"
-                                        Style="{StaticResource MaterialDesignRaisedDarkButton}"
-                                        Command="{Binding DataContext.SLockCommand,RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}" />
-                            </Grid>
-                            <Grid Grid.Column="1">
-                                <Grid.RowDefinitions>
                                     <RowDefinition Height="auto" />
-                                    <RowDefinition Height="*" />
+                                    <RowDefinition Height="auto" />
                                 </Grid.RowDefinitions>
-                                <TextBlock VerticalAlignment="Center"
-                                           Margin="10,0">
+                                <Grid.Resources>
+                                    <Style TargetType="Button"
+                                           BasedOn="{StaticResource MaterialDesignRaisedDarkButton}">
+                                        <Setter Property="Width"
+                                                Value="60" />
+                                        <Setter Property="Height"
+                                                Value="60" />
+                                        <Setter Property="FontSize"
+                                                Value="13" />
+                                        <Setter Property="Padding"
+                                                Value="0" />
+                                        <Setter Property="HorizontalAlignment"
+                                                Value="Center" />
+                                        <Setter Property="VerticalAlignment"
+                                                Value="Center" />
+                                        <Setter Property="Margin"
+                                                Value="2" />
+                                        <Setter Property="materialDesign:ButtonAssist.CornerRadius"
+                                                Value="10" />
+                                    </Style>
+                                </Grid.Resources>
+                                <StackPanel Grid.ColumnSpan="4"
+                                            Grid.Row="0"
+                                            Grid.Column="0"
+                                            VerticalAlignment="Center"
+                                            Orientation="Horizontal" Margin="30,0,0,0">
+                                    <Button Content="{lex:Loc 上使能}"
+                                            Grid.Row="2"
+                                            materialDesign:ButtonAssist.CornerRadius="10"
+                                            Style="{StaticResource MaterialDesignRaisedDarkButton}"
+                                            Command="{Binding MotorCommand}"
+                                            CommandParameter="True" />
+                                    <Button Content="{lex:Loc 下使能}"
+                                            Grid.Row="3"
+                                            Margin="10"
+                                            materialDesign:ButtonAssist.CornerRadius="10"
+                                            Style="{StaticResource MaterialDesignRaisedDarkButton}"
+                                            Command="{Binding MotorCommand}"
+                                            CommandParameter="False" />
+                                    <Button Content="{lex:Loc 复位错误}"
+                                            Grid.Row="4"
+                                            materialDesign:ButtonAssist.CornerRadius="10"
+                                            Style="{StaticResource MaterialDesignRaisedDarkButton}"
+                                            Command="{Binding ResetCommand}" />
+                                </StackPanel>
+                                <Button Grid.Row="2"
+                                        Grid.Column="0"
+                                        Command="{Binding JogCommand}"
+                                        CommandParameter="{Binding ElementName=txtDistance,Path=Value,Converter={StaticResource JogConverter}, ConverterParameter=X-}">
+                                    <StackPanel Orientation="Vertical">
+                                        <materialDesign:PackIcon Kind="ArrowLeftBold"
+                                                                 Width="26"
+                                                                 Height="26" />
+                                        <TextBlock Text="-X"
+                                                   HorizontalAlignment="Center" />
+                                    </StackPanel>
+                                </Button>
+                                <Button Grid.Row="2"
+                                        Grid.Column="2"
+                                        Command="{Binding JogCommand}"
+                                        CommandParameter="{Binding ElementName=txtDistance,Path=Value,Converter={StaticResource JogConverter}, ConverterParameter=X+}">
+                                    <StackPanel Orientation="Vertical">
+                                        <materialDesign:PackIcon Kind="ArrowRightBold"
+                                                                 Width="26"
+                                                                 Height="26" />
+                                        <TextBlock Text="+X"
+                                                   HorizontalAlignment="Center" />
+                                    </StackPanel>
+                                </Button>
+                                <Button Grid.Row="1"
+                                        Grid.Column="1"
+                                        Command="{Binding JogCommand}"
+                                        CommandParameter="{Binding ElementName=txtDistance,Path=Value,Converter={StaticResource JogConverter}, ConverterParameter=Y+}">
+                                    <StackPanel Orientation="Vertical">
+                                        <materialDesign:PackIcon Kind="ArrowTopBold"
+                                                                 Width="26"
+                                                                 Height="26" />
+                                        <TextBlock Text="+Y"
+                                                   HorizontalAlignment="Center" />
+                                    </StackPanel>
+                                </Button>
+                                <Button Grid.Row="3"
+                                        Grid.Column="1"
+                                        Command="{Binding JogCommand}"
+                                        CommandParameter="{Binding ElementName=txtDistance,Path=Value,Converter={StaticResource JogConverter}, ConverterParameter=Y-}">
+                                    <StackPanel Orientation="Vertical">
+                                        <materialDesign:PackIcon Kind="ArrowBottomBold"
+                                                                 Width="26"
+                                                                 Height="26" />
+                                        <TextBlock Text="-Y"
+                                                   HorizontalAlignment="Center" />
+                                    </StackPanel>
+                                </Button>
+                                <Button Grid.Row="1"
+                                        Grid.Column="3"
+                                        Command="{Binding JogCommand}"
+                                        CommandParameter="{Binding ElementName=txtDistance,Path=Value,Converter={StaticResource JogConverter}, ConverterParameter=Z+}">
+                                    <StackPanel Orientation="Vertical">
+                                        <materialDesign:PackIcon Kind="ArrowTopBold"
+                                                                 Width="26"
+                                                                 Height="26" />
+                                        <TextBlock Text="+Z"
+                                                   HorizontalAlignment="Center" />
+                                    </StackPanel>
+                                </Button>
+                                <Button Grid.Row="3"
+                                        Grid.Column="3"
+                                        Command="{Binding JogCommand}"
+                                        CommandParameter="{Binding ElementName=txtDistance,Path=Value,Converter={StaticResource JogConverter}, ConverterParameter=Z-}">
+                                    <StackPanel Orientation="Vertical">
+                                        <materialDesign:PackIcon Kind="ArrowBottomBold"
+                                                                 Width="26"
+                                                                 Height="26" />
+                                        <TextBlock Text="-Z"
+                                                   HorizontalAlignment="Center" />
+                                    </StackPanel>
+                                </Button>
+                                <Button Grid.Row="4"
+                                        Grid.Column="3"
+                                        Command="{Binding JogCommand}"
+                                        CommandParameter="{Binding ElementName=txtDistance,Path=Value,Converter={StaticResource JogConverter}, ConverterParameter=U+}">
+                                    <StackPanel Orientation="Vertical">
+                                        <materialDesign:PackIcon Kind="AxisZRotateCounterclockwise"
+                                                                 Width="26"
+                                                                 Height="26" />
+                                        <TextBlock Text="+U"
+                                                   HorizontalAlignment="Center" />
+                                    </StackPanel>
+                                </Button>
+                                <Button Grid.Row="4"
+                                        Grid.Column="0"
+                                        Command="{Binding JogCommand}"
+                                        CommandParameter="{Binding ElementName=txtDistance,Path=Value,Converter={StaticResource JogConverter}, ConverterParameter=U-}">
+                                    <StackPanel Orientation="Vertical">
+                                        <materialDesign:PackIcon Kind="AxisZRotateClockwise"
+                                                                 Width="26"
+                                                                 Height="26" />
+                                        <TextBlock Text="-U"
+                                                   HorizontalAlignment="Center" />
+                                    </StackPanel>
+                                </Button>
+
+                                <StackPanel Grid.Row="6"
+                                            Grid.ColumnSpan="7"
+                                            Margin="10,10,0,10"
+                                            Grid.Column="0"
+                                            VerticalAlignment="Center"
+                                            Orientation="Horizontal">
+                                    <TextBlock Text="{lex:Loc 步进距离}"
+                                               VerticalAlignment="Center" />
+                                    <uControl:myNumericUpDowm x:Name="txtDistance"
+                                                              MinWidth="100"
+                                                              Interval="0.1"
+                                                              Minimum="0"
+                                                              Maximum="100"
+                                                              StringFormat="{}{0:N3} mm"
+                                                              Value="{Binding Distance,Mode=TwoWay}"
+                                                              Margin="10,0" />
+                                </StackPanel>
+
+                                <TextBlock Grid.Row="7"
+                                           Grid.ColumnSpan="7"
+                                           Margin="10,10,0,10"
+                                           Grid.Column="0"
+                                           VerticalAlignment="Center"
+                                           HorizontalAlignment="Center">
                                     <TextBlock.Text>
                                         <MultiBinding StringFormat="{}CurPos X: {0:F3} mm Y: {1:F3} mm Z: {2:F3} mm U: {3:F3} mm">
                                             <Binding Path="Robot.CurrentPosition.X" />
@@ -1342,274 +1482,138 @@
                                         </MultiBinding>
                                     </TextBlock.Text>
                                 </TextBlock>
-                                <Grid Grid.Row="1">
-                                    <Grid.ColumnDefinitions>
-                                        <ColumnDefinition Width="auto" />
-                                        <ColumnDefinition Width="auto" />
-                                        <ColumnDefinition Width="auto" />
-                                        <ColumnDefinition Width="auto" />
-                                        <ColumnDefinition Width="auto" />
-                                        <ColumnDefinition Width="auto" />
-                                    </Grid.ColumnDefinitions>
-                                    <Grid.RowDefinitions>
-                                        <RowDefinition Height="auto" />
-                                        <RowDefinition Height="auto" />
-                                        <RowDefinition Height="auto" />
-                                        <RowDefinition Height="auto" />
-                                        <RowDefinition Height="auto" />
-                                        <RowDefinition Height="auto" />
-                                    </Grid.RowDefinitions>
-                                    <Grid.Resources>
-                                        <Style TargetType="Button"
-                                               BasedOn="{StaticResource MaterialDesignRaisedDarkButton}">
-                                            <Setter Property="Width"
-                                                    Value="60" />
-                                            <Setter Property="Height"
-                                                    Value="60" />
-                                            <Setter Property="FontSize"
-                                                    Value="13" />
-                                            <Setter Property="Padding"
-                                                    Value="0" />
-                                            <Setter Property="HorizontalAlignment"
-                                                    Value="Center" />
-                                            <Setter Property="VerticalAlignment"
-                                                    Value="Center" />
-                                            <Setter Property="Margin"
-                                                    Value="2" />
-                                            <Setter Property="materialDesign:ButtonAssist.CornerRadius"
-                                                    Value="10" />
-                                        </Style>
-                                    </Grid.Resources>
-
-                                    <Button x:Name="btn_x"
-                                            Grid.Row="1"
-                                            Grid.Column="0"
-                                            Command="{Binding DataContext.JogCommand,RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}"
-                                            CommandParameter="{Binding ElementName=txtDistance,Path=Value,Converter={StaticResource JogConverter}, ConverterParameter=X-}">
-                                        <StackPanel Orientation="Vertical">
-                                            <materialDesign:PackIcon Kind="ArrowLeftBold"
-                                                                     Width="26"
-                                                                     Height="26" />
-                                            <TextBlock Text="-X"
-                                                       HorizontalAlignment="Center" />
-                                        </StackPanel>
-                                    </Button>
-                                    <Button Grid.Row="1"
-                                            Grid.Column="2"
-                                            Command="{Binding DataContext.JogCommand,RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}"
-                                            CommandParameter="{Binding ElementName=txtDistance,Path=Value,Converter={StaticResource JogConverter}, ConverterParameter=X+}">
-                                        <StackPanel Orientation="Vertical">
-                                            <materialDesign:PackIcon Kind="ArrowRightBold"
-                                                                     Width="26"
-                                                                     Height="26" />
-                                            <TextBlock Text="+X"
-                                                       HorizontalAlignment="Center" />
-                                        </StackPanel>
-                                    </Button>
-                                    <Button Grid.Row="0"
-                                            Grid.Column="1"
-                                            Command="{Binding DataContext.JogCommand,RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}"
-                                            CommandParameter="{Binding ElementName=txtDistance,Path=Value,Converter={StaticResource JogConverter}, ConverterParameter=Y+}">
-                                        <StackPanel Orientation="Vertical">
-                                            <materialDesign:PackIcon Kind="ArrowTopBold"
-                                                                     Width="26"
-                                                                     Height="26" />
-                                            <TextBlock Text="+Y"
-                                                       HorizontalAlignment="Center" />
-                                        </StackPanel>
-                                    </Button>
-                                    <Button Grid.Row="2"
-                                            Grid.Column="1"
-                                            Command="{Binding DataContext.JogCommand,RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}"
-                                            CommandParameter="{Binding ElementName=txtDistance,Path=Value,Converter={StaticResource JogConverter}, ConverterParameter=Y-}">
-                                        <StackPanel Orientation="Vertical">
-                                            <materialDesign:PackIcon Kind="ArrowBottomBold"
-                                                                     Width="26"
-                                                                     Height="26" />
-                                            <TextBlock Text="-Y"
-                                                       HorizontalAlignment="Center" />
-                                        </StackPanel>
-                                    </Button>
-                                    <Button Grid.Row="0"
-                                            Grid.Column="3"
-                                            Command="{Binding DataContext.JogCommand,RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}"
-                                            CommandParameter="{Binding ElementName=txtDistance,Path=Value,Converter={StaticResource JogConverter}, ConverterParameter=Z+}">
-                                        <StackPanel Orientation="Vertical">
-                                            <materialDesign:PackIcon Kind="ArrowTopBold"
-                                                                     Width="26"
-                                                                     Height="26" />
-                                            <TextBlock Text="+Z"
-                                                       HorizontalAlignment="Center" />
-                                        </StackPanel>
-                                    </Button>
-                                    <Button Grid.Row="2"
-                                            Grid.Column="3"
-                                            Command="{Binding DataContext.JogCommand,RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}"
-                                            CommandParameter="{Binding ElementName=txtDistance,Path=Value,Converter={StaticResource JogConverter}, ConverterParameter=Z-}">
-                                        <StackPanel Orientation="Vertical">
-                                            <materialDesign:PackIcon Kind="ArrowBottomBold"
-                                                                     Width="26"
-                                                                     Height="26" />
-                                            <TextBlock Text="-Z"
-                                                       HorizontalAlignment="Center" />
-                                        </StackPanel>
-                                    </Button>
-                                    <Button Grid.Row="3"
-                                            Grid.Column="0"
-                                            Command="{Binding DataContext.JogCommand,RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}"
-                                            CommandParameter="{Binding ElementName=txtDistance,Path=Value,Converter={StaticResource JogConverter}, ConverterParameter=U+}">
-                                        <StackPanel Orientation="Vertical">
-                                            <materialDesign:PackIcon Kind="AxisZRotateCounterclockwise"
-                                                                     Width="26"
-                                                                     Height="26" />
-                                            <TextBlock Text="+U"
-                                                       HorizontalAlignment="Center" />
-                                        </StackPanel>
-                                    </Button>
-                                    <Button Grid.Row="4"
-                                            Grid.Column="0"
-                                            Command="{Binding DataContext.JogCommand,RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}"
-                                            CommandParameter="{Binding ElementName=txtDistance,Path=Value,Converter={StaticResource JogConverter}, ConverterParameter=U-}">
-                                        <StackPanel Orientation="Vertical">
-                                            <materialDesign:PackIcon Kind="AxisZRotateClockwise"
-                                                                     Width="26"
-                                                                     Height="26" />
-                                            <TextBlock Text="-U"
-                                                       HorizontalAlignment="Center" />
-                                        </StackPanel>
-                                    </Button>
-                                    <Button Grid.Row="3"
-                                            Grid.Column="1"
-                                            Grid.ColumnSpan="2"
-                                            Command="{Binding DataContext.JogCommand,RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}"
-                                            CommandParameter="{Binding ElementName=txtDistance,Path=Value,Converter={StaticResource JogConverter}, ConverterParameter=R+}">
-                                        <StackPanel Orientation="Vertical">
-                                            <materialDesign:PackIcon Kind="AxisZRotateCounterclockwise"
-                                                                     Width="26"
-                                                                     Height="26" />
-                                            <TextBlock Text="+R"
-                                                       HorizontalAlignment="Center" />
-                                        </StackPanel>
-                                    </Button>
-                                    <Button Grid.Row="4"
-                                            Grid.Column="1"
-                                            Grid.ColumnSpan="2"
-                                            Command="{Binding DataContext.JogCommand,RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}"
-                                            CommandParameter="{Binding ElementName=txtDistance,Path=Value,Converter={StaticResource JogConverter}, ConverterParameter=R-}">
-                                        <StackPanel Orientation="Vertical">
-                                            <materialDesign:PackIcon Kind="AxisZRotateClockwise"
-                                                                     Width="26"
-                                                                     Height="26" />
-                                            <TextBlock Text="-R"
-                                                       HorizontalAlignment="Center" />
-                                        </StackPanel>
-                                    </Button>
-
-                                    <Grid Grid.Row="5"
-                                          Grid.ColumnSpan="7"
-                                          Margin="10">
-                                        <Grid.RowDefinitions>
-                                            <RowDefinition Height="auto" />
-                                            <RowDefinition Height="auto" />
-                                            <RowDefinition Height="auto" />
-                                            <RowDefinition Height="auto" />
-                                        </Grid.RowDefinitions>
-                                        <Grid.ColumnDefinitions>
-                                            <ColumnDefinition Width="auto" />
-                                            <ColumnDefinition Width="auto" />
-                                            <ColumnDefinition Width="auto" />
-                                        </Grid.ColumnDefinitions>
-                                        <TextBlock Grid.Row="0"
-                                                   Grid.Column="0"
-                                                   Text="{lex:Loc 步进距离,Converter={StaticResource StringFormatConverter},ConverterParameter='{}{0}: '}"
-                                                   Margin="10"
-                                                   VerticalAlignment="Center"
-                                                   HorizontalAlignment="Right" />
-                                        <uControl:myNumericUpDowm x:Name="txtDistance"
-                                                                  Grid.Row="0"
-                                                                  Grid.Column="1"
-                                                                  MinWidth="100"
-                                                                  Value="{Binding DataContext.Distance,RelativeSource={RelativeSource AncestorType={x:Type UserControl}},Mode=TwoWay}"
-                                                                  Interval="0.1"
-                                                                  Minimum="0"
-                                                                  Maximum="100"
-                                                                  StringFormat="{}{0:N3} mm"
-                                                                  Margin="10"
-                                                                  materialDesign:HintAssist.Hint="{lex:Loc 步进距离}" />
+                            </Grid>
 
-                                        <TextBlock Grid.Row="1"
-                                                   Grid.Column="0"
-                                                   Text="{lex:Loc 点编号,Converter={StaticResource StringFormatConverter},ConverterParameter='{}{0}: '}"
-                                                   Margin="10"
-                                                   VerticalAlignment="Center" />
-                                        <ComboBox x:Name="cmbSelectPoint"
-                                                  Grid.Row="2"
-                                                  Grid.Column="0"
-                                                  MinWidth="80"
-                                                  materialDesign:HintAssist.Hint="{lex:Loc 选择点}"
-                                                  Margin="10,5"
-                                                  ItemsSource="{Binding Points}"
-                                                  SelectedItem="{Binding SelectedPointInComboBox,Mode=TwoWay}">
-                                            <ComboBox.ItemTemplate>
-                                                <DataTemplate>
-                                                    <StackPanel HorizontalAlignment="Left"
-                                                                Orientation="Horizontal">
-                                                        <TextBlock Text="{Binding Number,StringFormat={}{0}:}" />
-                                                        <TextBlock Text="{Binding Label}" />
-                                                    </StackPanel>
-                                                </DataTemplate>
-                                            </ComboBox.ItemTemplate>
-                                        </ComboBox>
+                            <Grid Grid.Row="1"
+                                  Grid.ColumnSpan="7">
+                                <Grid.RowDefinitions>
+                                    <RowDefinition Height="auto" />
+                                    <RowDefinition Height="auto" />
+                                    <RowDefinition Height="auto" />
+                                    <RowDefinition Height="auto" />
+                                </Grid.RowDefinitions>
+                                <Grid.ColumnDefinitions>
+                                    <ColumnDefinition Width="auto" />
+                                    <ColumnDefinition Width="auto" />
+                                    <ColumnDefinition Width="auto" />
+                                </Grid.ColumnDefinitions>
+                                <TextBlock Grid.Row="1"
+                                           Grid.Column="0"
+                                           Text="{lex:Loc 点编号,Converter={StaticResource StringFormatConverter},ConverterParameter='{}{0}: '}"
+                                           Margin="10"
+                                           VerticalAlignment="Center" />
+                                <ComboBox x:Name="cmbSelectPoint"
+                                          Grid.Row="2"
+                                          Grid.Column="0"
+                                          MinWidth="80"
+                                          materialDesign:HintAssist.Hint="{lex:Loc 选择点}"
+                                          Margin="10,5"
+                                          ItemsSource="{Binding Points}"
+                                          SelectedItem="{Binding SelectedPointInComboBox,Mode=TwoWay}">
+                                    <ComboBox.ItemTemplate>
+                                        <DataTemplate>
+                                            <StackPanel HorizontalAlignment="Left"
+                                                        Orientation="Horizontal">
+                                                <TextBlock Text="{Binding Number,StringFormat={}{0}:}" />
+                                                <TextBlock Text="{Binding Label}" />
+                                            </StackPanel>
+                                        </DataTemplate>
+                                    </ComboBox.ItemTemplate>
+                                </ComboBox>
 
-                                        <TextBlock Grid.Row="1"
-                                                   Grid.Column="1"
-                                                   Text="{lex:Loc 动作指令,Converter={StaticResource StringFormatConverter},ConverterParameter='{}{0}: '}"
-                                                   Margin="10"
-                                                   VerticalAlignment="Center" />
-                                        <ComboBox x:Name="cmbSelectMotioncmd"
-                                                  Grid.Row="2"
-                                                  Grid.Column="1"
-                                                  MinWidth="80"
-                                                  Margin="10,5"
-                                                  materialDesign:HintAssist.Hint="{lex:Loc 动作指令}"
-                                                  ItemsSource="{ext:EnumBindingSource EnumType=enums:MotionCommand}"
-                                                  SelectedItem="{Binding DataContext.SelectMotionCommand,RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}" />
+                                <TextBlock Grid.Row="1"
+                                           Grid.Column="1"
+                                           Text="{lex:Loc 动作指令,Converter={StaticResource StringFormatConverter},ConverterParameter='{}{0}: '}"
+                                           Margin="10"
+                                           VerticalAlignment="Center" />
+                                <ComboBox x:Name="cmbSelectMotioncmd"
+                                          Grid.Row="2"
+                                          Grid.Column="1"
+                                          MinWidth="80"
+                                          Margin="10,5"
+                                          materialDesign:HintAssist.Hint="{lex:Loc 动作指令}"
+                                          ItemsSource="{ext:EnumBindingSource EnumType=enums:MotionCommand}"
+                                          SelectedItem="{Binding DataContext.SelectMotionCommand,RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}" />
 
-                                        <Button Grid.Row="2"
-                                                Grid.Column="2"
-                                                Content="{lex:Loc 执行动作}"
-                                                Margin="2"
-                                                Command="{Binding DataContext.ExecuteMotion,RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}">
-                                            <Button.CommandParameter>
-                                                <MultiBinding Converter="{StaticResource MultiParameterConverter}">
-                                                    <Binding ElementName="cmbSelectPoint"
-                                                             Path="SelectedItem" />
-                                                    <Binding ElementName="cmbSelectMotioncmd"
-                                                             Path="SelectedItem" />
-                                                </MultiBinding>
-                                            </Button.CommandParameter>
-                                        </Button>
+                                <Button Grid.Row="2"
+                                        Grid.Column="2"
+                                        Content="{lex:Loc 执行动作}"
+                                        Margin="2"
+                                        Command="{Binding DataContext.ExecuteMotion,RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}">
+                                    <Button.CommandParameter>
+                                        <MultiBinding Converter="{StaticResource MultiParameterConverter}">
+                                            <Binding ElementName="cmbSelectPoint"
+                                                     Path="SelectedItem" />
+                                            <Binding ElementName="cmbSelectMotioncmd"
+                                                     Path="SelectedItem" />
+                                        </MultiBinding>
+                                    </Button.CommandParameter>
+                                </Button>
 
-                                        <Button Grid.Row="3"
-                                                Grid.Column="0"
-                                                Content="{lex:Loc 示教}"
-                                                Margin="2"
-                                                HorizontalAlignment="Right"
-                                                Command="{Binding DataContext.TechPointCommand,RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}" />
-                                        <!--<ToggleButton Grid.Row="3"
-                      Grid.Column="2"
-                      HorizontalAlignment="Right"
-                      VerticalAlignment="Center"
-                      Visibility="{Binding IsHaveRobot,Converter={StaticResource BooleanToVisibilityConverter}}"
-                      materialDesign:ToggleButtonAssist.OnContent="{materialDesign:PackIcon Kind=ArrowRight}"
-                      Content="{materialDesign:PackIcon Kind=ProgressWrench}"
-                      Style="{StaticResource MaterialDesignActionToggleButton}"
-                      ToolTip="{lex:Loc 手动验证流程}"
-                      Margin="5,0"
-                      IsChecked="{Binding IsRightDrawerOpen,ElementName=DrawerHost}" />-->
-                                    </Grid>
-                                </Grid>
+                                <Button Grid.Row="3"
+                                        Grid.Column="0"
+                                        Content="{lex:Loc 示教}"
+                                        Margin="2"
+                                        HorizontalAlignment="Right"
+                                        Command="{Binding DataContext.TechPointCommand,RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}" />
                             </Grid>
+                            <ItemsControl Grid.Row="2"
+                                          ItemsSource="{Binding AdditionalAxisList}">
+                                <ItemsControl.ItemTemplate>
+                                    <DataTemplate>
+                                        <Border Padding="6"
+                                                Margin="0,2"
+                                                Background="{DynamicResource MaterialDesignPaper}">
+                                            <DockPanel LastChildFill="False"
+                                                       VerticalAlignment="Center">
+                                                <StackPanel Orientation="Horizontal"
+                                                            DockPanel.Dock="Left"
+                                                            VerticalAlignment="Center">
+                                                    <TextBlock Text="{Binding Name}"
+                                                               Width="140"
+                                                               VerticalAlignment="Center" />
+                                                    <TextBlock Text="{Binding CurrentPosition, StringFormat='{}{0:F3} mm'}"
+                                                               Width="140"
+                                                               Margin="10,0"
+                                                               VerticalAlignment="Center" />
+                                                </StackPanel>
+                                                <StackPanel Orientation="Horizontal"
+                                                            HorizontalAlignment="Right"
+                                                            DockPanel.Dock="Right">
+                                                    <Button Content="-"
+                                                            Width="60"
+                                                            Height="36"
+                                                            Margin="6,0"
+                                                            Style="{StaticResource MaterialDesignRaisedDarkButton}"
+                                                            Command="{Binding DataContext.AdditionalAxisJogPlusCommand, RelativeSource={RelativeSource AncestorType=UserControl}}"
+                                                            CommandParameter="{Binding}" />
+                                                    <Button Content="+"
+                                                            Width="60"
+                                                            Height="36"
+                                                            Margin="6,0"
+                                                            Style="{StaticResource MaterialDesignRaisedDarkButton}"
+                                                            Command="{Binding DataContext.AdditionalAxisJogMinusCommand, RelativeSource={RelativeSource AncestorType=UserControl}}"
+                                                            CommandParameter="{Binding}" />
+                                                    <Button Content="上使能"
+                                                            Width="60"
+                                                            Height="36"
+                                                            Margin="6,0"
+                                                            Style="{StaticResource MaterialDesignRaisedDarkButton}"
+                                                            Command="{Binding DataContext.AdditionalAxisMotorOnCommand, RelativeSource={RelativeSource AncestorType=UserControl}}"
+                                                            CommandParameter="{Binding}" />
+                                                    <Button Content="下使能"
+                                                            Width="60"
+                                                            Height="36"
+                                                            Margin="6,0"
+                                                            Style="{StaticResource MaterialDesignRaisedDarkButton}"
+                                                            Command="{Binding DataContext.AdditionalAxisMotorOffCommand, RelativeSource={RelativeSource AncestorType=UserControl}}"
+                                                            CommandParameter="{Binding}" />
+                                                </StackPanel>
+                                            </DockPanel>
+                                        </Border>
+                                    </DataTemplate>
+                                </ItemsControl.ItemTemplate>
+                            </ItemsControl>
                         </Grid>
                     </Viewbox>
                 </Grid>