From bb1b76ee7a13708e72226cc59da3027c34d22fe2 Mon Sep 17 00:00:00 2001 From: QI Mingxuan Date: Thu, 7 May 2026 20:24:55 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=B0=84=E7=BA=BF=E6=BA=90?= =?UTF-8?q?=E6=8E=A2=E6=B5=8B=E5=99=A8Z=E8=BD=B4=E9=94=81=E5=AE=9A?= =?UTF-8?q?=E8=81=94=E5=8A=A8=E5=8A=9F=E8=83=BD=EF=BC=8C=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?=E5=AF=B9=E5=BA=94plc=E4=BF=A1=E5=8F=B7=E5=92=8C=E9=85=8D?= =?UTF-8?q?=E7=BD=AE=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- XP.Common/Controls/VirtualJoystick.cs | 30 +++-- .../Config/ConfigLoader.cs | 5 + .../Config/MotionControlConfig.cs | 18 +++ .../Config/MotionSignalNames.cs | 8 ++ .../Resources/Resources.en-US.resx | 26 +++-- .../Resources/Resources.resx | 16 +++ .../Resources/Resources.zh-CN.resx | 26 +++-- .../Resources/Resources.zh-TW.resx | 26 +++-- .../Resources/SZDZLockDisable.png | Bin 0 -> 7067 bytes .../Resources/SZDZLockEnable.png | Bin 0 -> 7562 bytes .../Services/IMotionControlService.cs | 18 +++ .../Services/MotionControlService.cs | 86 +++++++++++++- .../ViewModels/AxisControlViewModel.cs | 105 ++++++++++++++---- .../Views/AxisControlView.xaml | 77 +++++++++---- XP.Hardware.PLC/Services/PlcService.cs | 4 +- .../Views/RaySourceOperateView.xaml | 69 +++++++----- XplorePlane/App.config | 4 + 17 files changed, 404 insertions(+), 114 deletions(-) create mode 100644 XP.Hardware.MotionControl/Resources/SZDZLockDisable.png create mode 100644 XP.Hardware.MotionControl/Resources/SZDZLockEnable.png diff --git a/XP.Common/Controls/VirtualJoystick.cs b/XP.Common/Controls/VirtualJoystick.cs index 3066547..64b6e3b 100644 --- a/XP.Common/Controls/VirtualJoystick.cs +++ b/XP.Common/Controls/VirtualJoystick.cs @@ -4,6 +4,7 @@ using System.Windows.Controls; using System.Windows.Input; using System.Windows.Media; using System.Windows.Shapes; +using XP.Common.Logging.Interfaces; namespace XP.Common.Controls { @@ -31,13 +32,13 @@ namespace XP.Common.Controls /// 操控点元素引用 | Thumb element reference private Ellipse? _thumbElement; - #endregion + #endregion - #region 构造函数 | Constructor + #region 构造函数 | Constructor - public VirtualJoystick() + public VirtualJoystick() { - InitializeComponent(); + InitializeComponent(); // 控件加载完成后绑定操控点的 TranslateTransform | Bind thumb TranslateTransform after control loaded Loaded += (s, e) => @@ -46,8 +47,8 @@ namespace XP.Common.Controls if (_thumbElement != null) _thumbElement.RenderTransform = _thumbTransform; - // 初始化时更新背景和图标可见性 | Update background and icon visibility on init - UpdateIconVisibility(); + // 初始化时更新背景和图标可见性 | Update background and icon visibility on init + UpdateIconVisibility(); }; } @@ -152,7 +153,10 @@ namespace XP.Common.Controls private static void OnSwapMouseButtonsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { if (d is VirtualJoystick joystick) + { + joystick.ActiveMouseButton = MouseButtonType.None; joystick.UpdateIconVisibility(); + } } #endregion @@ -363,19 +367,23 @@ namespace XP.Common.Controls case MouseButtonType.Left: SetVisibility(Visibility.Collapsed, defaultTop, defaultBottom, defaultLeft, defaultRight); // 如果交换了左右键,这里显示的是右键图标组 - var leftIcons = SwapMouseButtons ? new[] { rightTop, rightBottom, rightLeft, rightRight } : new[] { leftTop, leftBottom, leftLeft, leftRight }; + var leftIcons = new[] { leftTop, leftBottom, leftLeft, leftRight }; SetVisibility(Visibility.Visible, leftIcons); - if (_thumbElement != null) _thumbElement.Fill = new SolidColorBrush(Color.FromRgb(0x3A, 0x7B, 0xC8)); + if (_thumbElement != null) _thumbElement.Fill = new SolidColorBrush(Color.FromRgb(0x2D, 0x63, 0x9E)); // 深一点的浅蓝色 | Darker light blue break; case MouseButtonType.Right: SetVisibility(Visibility.Collapsed, defaultTop, defaultBottom, defaultLeft, defaultRight); // 如果交换了左右键,这里显示的是左键图标组 - var rightIcons = SwapMouseButtons ? new[] { leftTop, leftBottom, leftLeft, leftRight } : new[] { rightTop, rightBottom, rightLeft, rightRight }; + var rightIcons = new[] { rightTop, rightBottom, rightLeft, rightRight }; SetVisibility(Visibility.Visible, rightIcons); - if (_thumbElement != null) _thumbElement.Fill = new SolidColorBrush(Color.FromRgb(0x5B, 0xA8, 0x5B)); + if (_thumbElement != null) _thumbElement.Fill = new SolidColorBrush(Color.FromRgb(0x45, 0x88, 0x45)); // 深一点的浅绿色 | Darker light green break; default: - if (_thumbElement != null) _thumbElement.Fill = new SolidColorBrush(Color.FromRgb(0x4A, 0x90, 0xD9)); + // 根据 SwapMouseButtons 决定中心按钮颜色 | Determine center button color based on SwapMouseButtons + if (_thumbElement != null) + _thumbElement.Fill = SwapMouseButtons + ? new SolidColorBrush(Color.FromRgb(0x5B, 0xA8, 0x5B)) // 浅绿色 | Light green + : new SolidColorBrush(Color.FromRgb(0x4A, 0x90, 0xD9)); // 浅蓝色 | Light blue break; } diff --git a/XP.Hardware.MotionControl/Config/ConfigLoader.cs b/XP.Hardware.MotionControl/Config/ConfigLoader.cs index 40d81f8..9f08bf1 100644 --- a/XP.Hardware.MotionControl/Config/ConfigLoader.cs +++ b/XP.Hardware.MotionControl/Config/ConfigLoader.cs @@ -66,6 +66,11 @@ namespace XP.Hardware.MotionControl.Config // 运行参数 | Runtime parameters config.PollingInterval = Int(get($"{P}:PollingInterval", "100")); config.DefaultVelocity = Int(get($"{P}:DefaultVelocity", "100")); + + // 射线源与探测器Z轴联动配置 | Source-Detector Z-axis linkage configuration + config.SourceDetectorZLinkage.Enabled = bool.TryParse(get($"{P}:SourceDetectorZLinkage:Enabled", "false"), out var enabled) && enabled; + config.SourceDetectorZLinkage.TriggerThreshold = Dbl(get($"{P}:SourceDetectorZLinkage:TriggerThreshold", "1.0")); + config.SourceDetectorZLinkage.SpeedPercent = Int(get($"{P}:SourceDetectorZLinkage:SpeedPercent", "100")); } private static LinearAxisConfig LoadLinear(string name, Func get) diff --git a/XP.Hardware.MotionControl/Config/MotionControlConfig.cs b/XP.Hardware.MotionControl/Config/MotionControlConfig.cs index f2a2473..9692d67 100644 --- a/XP.Hardware.MotionControl/Config/MotionControlConfig.cs +++ b/XP.Hardware.MotionControl/Config/MotionControlConfig.cs @@ -23,6 +23,24 @@ namespace XP.Hardware.MotionControl.Config /// 默认速度| Default velocity public int DefaultVelocity { get; set; } = 100; + + /// 射线源与探测器Z轴联动配置 | Source-Detector Z-axis linkage configuration + public SourceDetectorZLinkageConfig SourceDetectorZLinkage { get; set; } = new(); + } + + /// + /// 射线源与探测器Z轴联动配置 | Source-Detector Z-axis linkage configuration + /// + public class SourceDetectorZLinkageConfig + { + /// 联动启用 | Linkage enabled + public bool Enabled { get; set; } = false; + + /// 联动触发的位置变化阈值(mm)| Position change threshold for linkage trigger (mm) + public double TriggerThreshold { get; set; } = 1.0; + + /// 联动移动速度百分比(0-100)| Linkage movement speed percentage (0-100) + public int SpeedPercent { get; set; } = 100; } /// diff --git a/XP.Hardware.MotionControl/Config/MotionSignalNames.cs b/XP.Hardware.MotionControl/Config/MotionSignalNames.cs index 638e9d8..153e17c 100644 --- a/XP.Hardware.MotionControl/Config/MotionSignalNames.cs +++ b/XP.Hardware.MotionControl/Config/MotionSignalNames.cs @@ -6,6 +6,14 @@ namespace XP.Hardware.MotionControl.Config /// public static class MotionSignalNames { + // ==================== 射线源与探测器Z轴联动 | Source-Detector Z-axis Linkage ==================== + /// 射线源与探测器Z轴联动使能(写入)| Source-Detector Z-axis linkage enable (write) + public const string SourceDetZ_Linkage_Enable = "MC_SourceDetZ_Linkage_Enable"; + /// 虚拟摇杆使能(写入)| Virtual joystick enable (write) + public const string VirtualJoystick_Enable = "MC_VirtualJoystick_Enable"; + /// 实体摇杆输入激活(读取)| Physical joystick input active (read) + public const string Joystick_Active = "MC_Joystick_Active"; + // ==================== 直线轴 SourceZ | Linear Axis SourceZ ==================== public const string SourceZ_Pos = "MC_SourceZ_Pos"; public const string SourceZ_Target = "MC_SourceZ_Target"; diff --git a/XP.Hardware.MotionControl/Resources/Resources.en-US.resx b/XP.Hardware.MotionControl/Resources/Resources.en-US.resx index 90aaa7d..5208de7 100644 --- a/XP.Hardware.MotionControl/Resources/Resources.en-US.resx +++ b/XP.Hardware.MotionControl/Resources/Resources.en-US.resx @@ -262,25 +262,25 @@ Confirm to filll move matrix? Axis Positions - X mm + X - Y mm + Y - SZ mm + SZ - DZ mm + DZ - DT' ° + DT - R' ° + R - FR' ° + FR Safety Parameters @@ -303,4 +303,16 @@ Confirm to filll move matrix? Motion Control + + mm + + + ° + + + Switch the way the virtual joystick is controlled when using the mouse left and right buttons + + + Error + \ No newline at end of file diff --git a/XP.Hardware.MotionControl/Resources/Resources.resx b/XP.Hardware.MotionControl/Resources/Resources.resx index 42aa50b..aab981e 100644 --- a/XP.Hardware.MotionControl/Resources/Resources.resx +++ b/XP.Hardware.MotionControl/Resources/Resources.resx @@ -361,4 +361,20 @@ DetectorZ → {4:F2}mm 运动控制 header + + mm + 长度单位 + + + ° + 角度单位 + + + 切换虚拟摇杆鼠标左右键控制方式 + 切换虚拟摇杆鼠标左右键控制方式 + + + 错误 + 错误 + \ No newline at end of file diff --git a/XP.Hardware.MotionControl/Resources/Resources.zh-CN.resx b/XP.Hardware.MotionControl/Resources/Resources.zh-CN.resx index 067d0d6..5bdc199 100644 --- a/XP.Hardware.MotionControl/Resources/Resources.zh-CN.resx +++ b/XP.Hardware.MotionControl/Resources/Resources.zh-CN.resx @@ -262,25 +262,25 @@ DetectorZ → {4:F2}mm 轴位置 - X mm + X - Y mm + Y - SZ mm + SZ - DZ mm + DZ - DT' ° + DT - R' ° + R - FR' ° + FR 安全参数 @@ -303,4 +303,16 @@ DetectorZ → {4:F2}mm 运动控制 + + mm + + + ° + + + 切换虚拟摇杆鼠标左右键控制方式 + + + 错误 + \ No newline at end of file diff --git a/XP.Hardware.MotionControl/Resources/Resources.zh-TW.resx b/XP.Hardware.MotionControl/Resources/Resources.zh-TW.resx index 390cd2a..c8b9b06 100644 --- a/XP.Hardware.MotionControl/Resources/Resources.zh-TW.resx +++ b/XP.Hardware.MotionControl/Resources/Resources.zh-TW.resx @@ -262,25 +262,25 @@ DetectorZ → {4:F2}mm 軸位置 - X mm + X - Y mm + Y - SZ mm + SZ - DZ mm + DZ - DT' ° + DT - R' ° + R - FR' ° + FR 安全參數 @@ -303,4 +303,16 @@ DetectorZ → {4:F2}mm 運動控制 + + mm + + + ° + + + 切換虛擬搖杆喺使用滑鼠左右鍵控制時嘅方式 + + + 錯誤 + \ No newline at end of file diff --git a/XP.Hardware.MotionControl/Resources/SZDZLockDisable.png b/XP.Hardware.MotionControl/Resources/SZDZLockDisable.png new file mode 100644 index 0000000000000000000000000000000000000000..76449994121d8c3bd4ae6ca077ebd250fb979e60 GIT binary patch literal 7067 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 + /// 启用/禁用射线源与探测器Z轴联动 | Enable/disable Source-Detector Z-axis linkage + /// + /// true=启用联动,false=禁用联动 | true=enable linkage, false=disable linkage + /// 操作结果 | Operation result + MotionResult SetSourceDetectorZLinkage(bool enabled); + + /// + /// 设置虚拟摇杆使能 | Set virtual joystick enable + /// + /// true=启用虚拟摇杆,false=禁用虚拟摇杆 | true=enable virtual joystick, false=disable virtual joystick + /// 操作结果 | Operation result + MotionResult SetVirtualJoystickEnable(bool enabled); + + #endregion } } diff --git a/XP.Hardware.MotionControl/Services/MotionControlService.cs b/XP.Hardware.MotionControl/Services/MotionControlService.cs index 870af8b..de58499 100644 --- a/XP.Hardware.MotionControl/Services/MotionControlService.cs +++ b/XP.Hardware.MotionControl/Services/MotionControlService.cs @@ -1,9 +1,10 @@ -using System; +using Emgu.CV.Dnn; +using Prism.Events; +using System; using System.Collections.Generic; using System.Linq; using System.Threading; using System.Windows; -using Prism.Events; using XP.Common.Localization; using XP.Common.Logging.Interfaces; using XP.Hardware.MotionControl.Abstractions; @@ -27,9 +28,10 @@ namespace XP.Hardware.MotionControl.Services private readonly IEventAggregator _eventAggregator; private readonly ILoggerService _logger; private readonly IPlcService _plcService; + private readonly ISignalDataService _signalService; - // 轮询定时器 | Polling timer - private Timer _pollingTimer; + // 轮询定时器 | Polling timer + private Timer _pollingTimer; private int _pollErrorCount = 0; /// @@ -58,7 +60,8 @@ namespace XP.Hardware.MotionControl.Services MotionControlConfig config, IEventAggregator eventAggregator, ILoggerService logger, - IPlcService plcService) + IPlcService plcService, + ISignalDataService signalService) { _motionSystem = motionSystem ?? throw new ArgumentNullException(nameof(motionSystem)); _geometryCalculator = geometryCalculator ?? throw new ArgumentNullException(nameof(geometryCalculator)); @@ -68,7 +71,8 @@ namespace XP.Hardware.MotionControl.Services _logger = (logger ?? throw new ArgumentNullException(nameof(logger))) .ForModule(); _plcService = plcService ?? throw new ArgumentNullException(nameof(plcService)); - } + _signalService = signalService ?? throw new ArgumentNullException(nameof(signalService)); + } #region 轮询控制 | Polling Control @@ -544,6 +548,76 @@ namespace XP.Hardware.MotionControl.Services #endregion + #region 射线源与探测器Z轴联动 | Source-Detector Z-axis Linkage + + /// + /// 启用/禁用射线源与探测器Z轴联动 | Enable/disable Source-Detector Z-axis linkage + /// + /// true=启用联动,false=禁用联动 | true=enable linkage, false=disable linkage + /// 操作结果 | Operation result + public MotionResult SetSourceDetectorZLinkage(bool enabled) + { + try + { + var config = _config.SourceDetectorZLinkage; + if (!config.Enabled) + { + _logger.Warn("射线源与探测器Z轴联动未在配置中启用 | Source-Detector Z-axis linkage not enabled in config"); + return MotionResult.Fail("射线源与探测器Z轴联动未启用 | Source-Detector Z-axis linkage not enabled"); + } + + // 写入联动使能信号 | Write linkage enable signal + var result = _signalService.EnqueueWrite(MotionSignalNames.SourceDetZ_Linkage_Enable, (bool)enabled); + + if (result) + { + _logger.Info("射线源与探测器Z轴联动已{Enabled} | Source-Detector Z-axis linkage {Enabled}", + enabled ? "启用 | enabled" : "禁用 | disabled"); + } + else + { + _logger.Warn("射线源与探测器Z轴联动使能写入失败 | Source-Detector Z-axis linkage enable write failed"); + } + + return result ? MotionResult.Ok() : MotionResult.Fail("联动使能写入失败 | Linkage enable write failed"); + } + catch (Exception ex) + { + _logger.Error(ex, "射线源与探测器Z轴联动设置异常: {Message} | Source-Detector Z-axis linkage setting error: {Message}", ex.Message); + return MotionResult.Fail($"联动设置异常: {ex.Message}"); + } + } + + /// + /// 设置虚拟摇杆使能 | Set virtual joystick enable + /// + /// true=启用虚拟摇杆,false=禁用虚拟摇杆 | true=enable virtual joystick, false=disable virtual joystick + /// 操作结果 | Operation result + public MotionResult SetVirtualJoystickEnable(bool enabled) + { + try + { + var result = _signalService.EnqueueWrite(MotionSignalNames.VirtualJoystick_Enable, (bool)enabled); + if (result) + { + _logger.Info("虚拟摇杆已{Enabled} | Virtual joystick {Enabled}", + enabled ? "启用 | enabled" : "禁用 | disabled"); + } + else + { + _logger.Warn("虚拟摇杆使能写入失败 | Virtual joystick enable write failed"); + } + + return result ? MotionResult.Ok() : MotionResult.Fail("虚拟摇杆使能写入失败 | Virtual joystick enable write failed"); + } + catch (Exception ex) + { + _logger.Error(ex, "虚拟摇杆使能设置异常: {Message} | Virtual joystick enable setting error: {Message}", ex.Message); + return MotionResult.Fail($"虚拟摇杆使能设置异常: {ex.Message}"); + } + } + #endregion + #region 安全门控制 | Safety Door Control /// diff --git a/XP.Hardware.MotionControl/ViewModels/AxisControlViewModel.cs b/XP.Hardware.MotionControl/ViewModels/AxisControlViewModel.cs index 4c7ce5e..bd3272b 100644 --- a/XP.Hardware.MotionControl/ViewModels/AxisControlViewModel.cs +++ b/XP.Hardware.MotionControl/ViewModels/AxisControlViewModel.cs @@ -85,9 +85,11 @@ namespace XP.Hardware.MotionControl.ViewModels ToggleSwapMouseButtonsCommand = new DelegateCommand(ExecuteToggleSwapMouseButtons); SavePositionsCommand = new DelegateCommand(ExecuteSavePositions); RestorePositionsCommand = new DelegateCommand(ExecuteRestorePositions, () => _savedPositions != null && IsPlcConnected); + // 射线源与探测器Z轴联动命令 | Source-Detector Z-axis linkage command + SZDZLockCommand = new DelegateCommand(() => SafeRun(ExecuteSZDZLock), () => IsPlcConnected); - // 初始化 Jog 状态跟踪字典 | Initialize jog state tracking dictionaries - foreach (AxisId axisId in Enum.GetValues(typeof(AxisId))) + // 初始化 Jog 状态跟踪字典 | Initialize jog state tracking dictionaries + foreach (AxisId axisId in Enum.GetValues(typeof(AxisId))) _linearJogActive[axisId] = false; foreach (RotaryAxisId axisId in Enum.GetValues(typeof(RotaryAxisId))) _rotaryJogActive[axisId] = false; @@ -267,12 +269,16 @@ namespace XP.Hardware.MotionControl.ViewModels /// 错误提示信息 | Error message public string ErrorMessage { get => _errorMessage; set => SetProperty(ref _errorMessage, value); } - #endregion + // 射线源与探测器Z轴联动状态 | Source-Detector Z-axis linkage state + private bool _szdzLock; + public bool SZDZLock { get => _szdzLock; set => SetProperty(ref _szdzLock, value); } - #region 保存位置状态 | Saved Positions State + #endregion - /// 是否有保存的位置数据 | Whether saved position data exists - public bool HasSavedPositions => _savedPositions != null; + #region 保存位置状态 | Saved Positions State + + /// 是否有保存的位置数据 | Whether saved position data exists + public bool HasSavedPositions => _savedPositions != null; #endregion @@ -290,14 +296,16 @@ namespace XP.Hardware.MotionControl.ViewModels /// 恢复保存的轴位置命令 | Restore saved axis positions command public DelegateCommand RestorePositionsCommand { get; } - #endregion + /// 射线源与探测器Z轴锁定移动命令 | Source-Detector Z-axis lock move command + public DelegateCommand SZDZLockCommand { get; } + #endregion - #region 事件回调 | Event Callbacks + #region 事件回调 | Event Callbacks - /// - /// 几何参数更新回调,刷新轴实际位置 | Geometry updated callback, refresh axis actual positions - /// - private void OnGeometryUpdated(GeometryData data) + /// + /// 几何参数更新回调,刷新轴实际位置 | Geometry updated callback, refresh axis actual positions + /// + private void OnGeometryUpdated(GeometryData data) { try { @@ -453,8 +461,24 @@ namespace XP.Hardware.MotionControl.ViewModels { IsJoystickEnabled = !IsJoystickEnabled; _logger.Info("摇杆使能状态切换:{Enabled} | Joystick enable toggled: {Enabled}", IsJoystickEnabled); - // TODO: 发送使能状态到 PLC(根据实际 PLC 信号定义)| Send enable state to PLC (based on actual PLC signal definition) - } + // 发送使能状态到 PLC(根据实际 PLC 信号定义)| Send enable state to PLC (based on actual PLC signal definition) + try + { + // 设置虚拟摇杆使能 | Set virtual joystick enable + var result = _motionControlService.SetVirtualJoystickEnable(IsJoystickEnabled); + + if (!result.Success) + { + MessageBox.Show(result.ErrorMessage, XP.Common.Localization.LocalizationHelper.Get("MC_Error"), + MessageBoxButton.OK, MessageBoxImage.Warning); + } + } + catch (Exception ex) + { + MessageBox.Show(ex.Message, XP.Common.Localization.LocalizationHelper.Get("MC_Error"), + MessageBoxButton.OK, MessageBoxImage.Error); + } + } /// /// 切换左右键交换状态 | Toggle swap mouse buttons state @@ -515,15 +539,42 @@ namespace XP.Hardware.MotionControl.ViewModels _logger.Info("轴位置已恢复并发送移动命令 | Axis positions restored and move commands sent"); } - #endregion + /// 射线源与探测器Z轴锁定移动 | Source-Detector Z-axis lock move + private void ExecuteSZDZLock() + { + try + { + // 切换联动状态 | Toggle linkage state + SZDZLock = !SZDZLock; - #region 摇杆 Jog 映射逻辑 | Joystick Jog Mapping Logic + // 调用服务设置联动 | Call service to set linkage + var result = _motionControlService.SetSourceDetectorZLinkage(SZDZLock); - /// - /// 处理双轴摇杆输出变化,根据当前激活按键映射到对应轴的 Jog 操作 - /// Handle dual joystick output changes, map to corresponding axis jog based on active button - /// - private void HandleDualJoystickOutput() + if (!result.Success) + { + // 如果设置失败,恢复状态 | If set failed, restore state + SZDZLock = !SZDZLock; + MessageBox.Show(result.ErrorMessage, XP.Common.Localization.LocalizationHelper.Get("MC_Error"), + MessageBoxButton.OK, MessageBoxImage.Warning); + } + } + catch (Exception ex) + { + // 异常时恢复状态 | Restore state on exception + SZDZLock = !SZDZLock; + MessageBox.Show(ex.Message, XP.Common.Localization.LocalizationHelper.Get("MC_Error"), + MessageBoxButton.OK, MessageBoxImage.Error); + } + } + #endregion + + #region 摇杆 Jog 映射逻辑 | Joystick Jog Mapping Logic + + /// + /// 处理双轴摇杆输出变化,根据当前激活按键映射到对应轴的 Jog 操作 + /// Handle dual joystick output changes, map to corresponding axis jog based on active button + /// + private void HandleDualJoystickOutput() { switch (DualJoystickActiveButton) { @@ -555,12 +606,20 @@ namespace XP.Hardware.MotionControl.ViewModels case MouseButtonType.Left: // 左键:Y→SourceZ Jog | Left button: Y→SourceZ Jog UpdateLinearJog(AxisId.SourceZ, SingleJoystickOutputY); + if (_szdzLock) + { + UpdateLinearJog(AxisId.DetectorZ, SingleJoystickOutputY); + } break; case MouseButtonType.Right: // 右键:Y→DetectorZ Jog | Right button: Y→DetectorZ Jog - UpdateLinearJog(AxisId.DetectorZ, SingleJoystickOutputY); - break; + UpdateLinearJog(AxisId.DetectorZ, SingleJoystickOutputY); + if (_szdzLock) + { + UpdateLinearJog(AxisId.SourceZ, SingleJoystickOutputY); + } + break; } } diff --git a/XP.Hardware.MotionControl/Views/AxisControlView.xaml b/XP.Hardware.MotionControl/Views/AxisControlView.xaml index 325e094..eeefc55 100644 --- a/XP.Hardware.MotionControl/Views/AxisControlView.xaml +++ b/XP.Hardware.MotionControl/Views/AxisControlView.xaml @@ -47,6 +47,7 @@ + @@ -60,26 +61,31 @@ - - + + + - - + + + + + + - - + + + + + + - - + + + - - - - - - - - + + + @@ -120,10 +126,12 @@ + + - @@ -148,7 +156,7 @@ - @@ -156,14 +164,39 @@ + + + + + + +