using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.Linq; using System.Windows; using TeamAAS.Motion.Models; namespace TeamAAS.Views { public partial class EditMotionAxesDialog { public ObservableCollection Rows { get; } = new ObservableCollection(); public List Result { get; private set; } public EditMotionAxesDialog(IEnumerable axes) { InitializeComponent(); DataContext = this; foreach (var cfg in axes ?? Enumerable.Empty()) Rows.Add(AxisEditRow.FromConfig(cfg)); if (Rows.Count == 0) { foreach (var cfg in MotionAxisCatalog.CreateDefaults(4)) Rows.Add(AxisEditRow.FromConfig(cfg)); } } private void BtnAdd_Click(object sender, RoutedEventArgs e) { int next = Rows.Count == 0 ? 0 : Rows.Max(r => r.AxisNo) + 1; Rows.Add(new AxisEditRow { AxisNo = next, Name = next < MotionAxisCatalog.DefaultNames.Length ? MotionAxisCatalog.DefaultNames[next] : $"Axis-{next}", KindName = "实轴", OutputChannel = next }); } private void BtnAddCombined_Click(object sender, RoutedEventArgs e) { var selectedItems = GridAxes.SelectedItems.Cast().ToList(); var master = GridAxes.SelectedItem as AxisEditRow; if (master == null || master.Kind != MotionAxisKind.Virtual) master = selectedItems.FirstOrDefault(r => r.Kind == MotionAxisKind.Virtual); if (master == null || master.Kind != MotionAxisKind.Virtual) { DialogHelper.ShowWarning("请先选中一条虚轴作为主轴,再添加从轴"); return; } var existing = new HashSet(master.ToConfig().ParseMembers().Select(m => m.AxisNo)) { master.AxisNo }; var slaves = selectedItems .Where(r => r != master && !r.ToConfig().IsGrouped) .Select(r => r.AxisNo) .Where(no => !existing.Contains(no)) .Distinct() .ToList(); if (slaves.Count == 0) { var candidate = Rows.FirstOrDefault(r => r != master && !r.ToConfig().IsGrouped && !existing.Contains(r.AxisNo)); if (candidate != null) slaves.Add(candidate.AxisNo); } if (slaves.Count == 0) { DialogHelper.ShowWarning("没有可添加的从轴。请先添加一条实轴或未跟随的虚轴"); return; } var parts = string.IsNullOrWhiteSpace(master.CombinedMembers) ? new List() : master.CombinedMembers .Split(new[] { ',', ';', ' ', ',' }, StringSplitOptions.RemoveEmptyEntries) .Select(s => s.Trim()) .Where(s => s.Length > 0) .ToList(); foreach (var no in slaves) { if (!existing.Contains(no)) parts.Add(no.ToString()); } master.CombinedMembers = string.Join(",", parts); GridAxes.SelectedItem = master; GridAxes.ScrollIntoView(master); DialogHelper.Info($"虚轴 {master.Name} 已添加从轴:{string.Join(",", slaves)}"); } private void BtnRemove_Click(object sender, RoutedEventArgs e) { var selected = GridAxes.SelectedItems.Cast().ToList(); foreach (var row in selected) Rows.Remove(row); } private void BtnOK_Click(object sender, RoutedEventArgs e) { string err = Validate(); if (err != null) { DialogHelper.ShowWarning(err); return; } Result = Rows.Select(r => r.ToConfig()).ToList(); DialogResult = true; } private string Validate() { if (Rows.Count == 0) return "至少配置一条轴"; var nos = new HashSet(); foreach (var row in Rows) { if (row.AxisNo < 0 || row.AxisNo > 63) return $"轴号 {row.AxisNo} 超出范围 [0,63]"; if (!nos.Add(row.AxisNo)) return $"轴号 {row.AxisNo} 重复"; if (string.IsNullOrWhiteSpace(row.Name)) return $"轴 {row.AxisNo} 名称为空"; } var leafNos = new HashSet(Rows.Where(r => !r.ToConfig().IsGrouped).Select(r => r.AxisNo)); foreach (var row in Rows) { var cfg = row.ToConfig(); var members = cfg.ParseMembers(); bool wantGroup = cfg.Kind == MotionAxisKind.Combined || (cfg.Kind == MotionAxisKind.Virtual && !string.IsNullOrWhiteSpace(cfg.CombinedMembers)); if (!wantGroup) continue; if (members.Count < 1) return $"{cfg.KindText} {row.Name} 至少需要 1 个从轴"; foreach (var m in members) { if (m.AxisNo == row.AxisNo) return $"{row.Name} 不能把自身当作从轴"; if (!leafNos.Contains(m.AxisNo)) return $"{row.Name} 的从轴 {m.AxisNo} 必须是已有的实轴或未跟随的虚轴"; } } return null; } } public class AxisEditRow : INotifyPropertyChanged { private int _axisNo; private string _name = "Axis"; private MotionAxisKind _kind; private int _outputChannel; private string _combinedMembers = ""; private double _velocity = 100; private bool _softLimitEnabled; private double _softLimitNeg; private double _softLimitPos; private double _maxVelocity; public int AxisNo { get => _axisNo; set { _axisNo = value; OnChanged(nameof(AxisNo)); } } public string Name { get => _name; set { _name = value; OnChanged(nameof(Name)); } } public MotionAxisKind Kind { get => _kind; set { _kind = value; OnChanged(nameof(Kind)); OnChanged(nameof(KindName)); } } public string KindName { get { switch (_kind) { case MotionAxisKind.Virtual: return "虚轴"; case MotionAxisKind.Combined: return "组合轴"; default: return "实轴"; } } set { if (value == "虚轴") _kind = MotionAxisKind.Virtual; else if (value == "组合轴") _kind = MotionAxisKind.Combined; else _kind = MotionAxisKind.Physical; OnChanged(nameof(Kind)); OnChanged(nameof(KindName)); } } public int OutputChannel { get => _outputChannel; set { _outputChannel = value; OnChanged(nameof(OutputChannel)); } } public string CombinedMembers { get => _combinedMembers; set { _combinedMembers = value; OnChanged(nameof(CombinedMembers)); } } public double Velocity { get => _velocity; set { _velocity = value; OnChanged(nameof(Velocity)); } } public bool SoftLimitEnabled { get => _softLimitEnabled; set { _softLimitEnabled = value; OnChanged(nameof(SoftLimitEnabled)); } } public double SoftLimitNeg { get => _softLimitNeg; set { _softLimitNeg = value; OnChanged(nameof(SoftLimitNeg)); } } public double SoftLimitPos { get => _softLimitPos; set { _softLimitPos = value; OnChanged(nameof(SoftLimitPos)); } } public double MaxVelocity { get => _maxVelocity; set { _maxVelocity = value; OnChanged(nameof(MaxVelocity)); } } public event PropertyChangedEventHandler PropertyChanged; private void OnChanged(string name) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name)); public static AxisEditRow FromConfig(MotionAxisConfig cfg) { cfg = cfg ?? new MotionAxisConfig(); return new AxisEditRow { AxisNo = cfg.AxisNo, Name = cfg.Name, Kind = cfg.Kind, OutputChannel = cfg.OutputChannel, CombinedMembers = cfg.CombinedMembers, Velocity = cfg.Velocity, SoftLimitEnabled = cfg.SoftLimitEnabled, SoftLimitNeg = cfg.SoftLimitNeg, SoftLimitPos = cfg.SoftLimitPos, MaxVelocity = cfg.MaxVelocity }; } public MotionAxisConfig ToConfig() { return new MotionAxisConfig { AxisNo = AxisNo, Name = Name?.Trim(), Kind = Kind, OutputChannel = OutputChannel, CombinedMembers = CombinedMembers?.Trim() ?? "", Velocity = Velocity, SoftLimitEnabled = SoftLimitEnabled, SoftLimitNeg = SoftLimitNeg, SoftLimitPos = SoftLimitPos, MaxVelocity = MaxVelocity }; } } }