EditMotionAxesDialog.xaml.cs 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Collections.ObjectModel;
  4. using System.ComponentModel;
  5. using System.Linq;
  6. using System.Windows;
  7. using TeamAAS.Motion.Models;
  8. namespace TeamAAS.Views
  9. {
  10. public partial class EditMotionAxesDialog
  11. {
  12. public ObservableCollection<AxisEditRow> Rows { get; } = new ObservableCollection<AxisEditRow>();
  13. public List<MotionAxisConfig> Result { get; private set; }
  14. public EditMotionAxesDialog(IEnumerable<MotionAxisConfig> axes)
  15. {
  16. InitializeComponent();
  17. DataContext = this;
  18. foreach (var cfg in axes ?? Enumerable.Empty<MotionAxisConfig>())
  19. Rows.Add(AxisEditRow.FromConfig(cfg));
  20. if (Rows.Count == 0)
  21. {
  22. foreach (var cfg in MotionAxisCatalog.CreateDefaults(4))
  23. Rows.Add(AxisEditRow.FromConfig(cfg));
  24. }
  25. }
  26. private void BtnAdd_Click(object sender, RoutedEventArgs e)
  27. {
  28. int next = Rows.Count == 0 ? 0 : Rows.Max(r => r.AxisNo) + 1;
  29. Rows.Add(new AxisEditRow
  30. {
  31. AxisNo = next,
  32. Name = next < MotionAxisCatalog.DefaultNames.Length ? MotionAxisCatalog.DefaultNames[next] : $"Axis-{next}",
  33. KindName = "实轴",
  34. OutputChannel = next
  35. });
  36. }
  37. private void BtnAddCombined_Click(object sender, RoutedEventArgs e)
  38. {
  39. var selectedItems = GridAxes.SelectedItems.Cast<AxisEditRow>().ToList();
  40. var master = GridAxes.SelectedItem as AxisEditRow;
  41. if (master == null || master.Kind != MotionAxisKind.Virtual)
  42. master = selectedItems.FirstOrDefault(r => r.Kind == MotionAxisKind.Virtual);
  43. if (master == null || master.Kind != MotionAxisKind.Virtual)
  44. {
  45. DialogHelper.ShowWarning("请先选中一条虚轴作为主轴,再添加从轴");
  46. return;
  47. }
  48. var existing = new HashSet<int>(master.ToConfig().ParseMembers().Select(m => m.AxisNo))
  49. {
  50. master.AxisNo
  51. };
  52. var slaves = selectedItems
  53. .Where(r => r != master && !r.ToConfig().IsGrouped)
  54. .Select(r => r.AxisNo)
  55. .Where(no => !existing.Contains(no))
  56. .Distinct()
  57. .ToList();
  58. if (slaves.Count == 0)
  59. {
  60. var candidate = Rows.FirstOrDefault(r =>
  61. r != master && !r.ToConfig().IsGrouped && !existing.Contains(r.AxisNo));
  62. if (candidate != null)
  63. slaves.Add(candidate.AxisNo);
  64. }
  65. if (slaves.Count == 0)
  66. {
  67. DialogHelper.ShowWarning("没有可添加的从轴。请先添加一条实轴或未跟随的虚轴");
  68. return;
  69. }
  70. var parts = string.IsNullOrWhiteSpace(master.CombinedMembers)
  71. ? new List<string>()
  72. : master.CombinedMembers
  73. .Split(new[] { ',', ';', ' ', ',' }, StringSplitOptions.RemoveEmptyEntries)
  74. .Select(s => s.Trim())
  75. .Where(s => s.Length > 0)
  76. .ToList();
  77. foreach (var no in slaves)
  78. {
  79. if (!existing.Contains(no))
  80. parts.Add(no.ToString());
  81. }
  82. master.CombinedMembers = string.Join(",", parts);
  83. GridAxes.SelectedItem = master;
  84. GridAxes.ScrollIntoView(master);
  85. DialogHelper.Info($"虚轴 {master.Name} 已添加从轴:{string.Join(",", slaves)}");
  86. }
  87. private void BtnRemove_Click(object sender, RoutedEventArgs e)
  88. {
  89. var selected = GridAxes.SelectedItems.Cast<AxisEditRow>().ToList();
  90. foreach (var row in selected)
  91. Rows.Remove(row);
  92. }
  93. private void BtnOK_Click(object sender, RoutedEventArgs e)
  94. {
  95. string err = Validate();
  96. if (err != null)
  97. {
  98. DialogHelper.ShowWarning(err);
  99. return;
  100. }
  101. Result = Rows.Select(r => r.ToConfig()).ToList();
  102. DialogResult = true;
  103. }
  104. private string Validate()
  105. {
  106. if (Rows.Count == 0) return "至少配置一条轴";
  107. var nos = new HashSet<int>();
  108. foreach (var row in Rows)
  109. {
  110. if (row.AxisNo < 0 || row.AxisNo > 63) return $"轴号 {row.AxisNo} 超出范围 [0,63]";
  111. if (!nos.Add(row.AxisNo)) return $"轴号 {row.AxisNo} 重复";
  112. if (string.IsNullOrWhiteSpace(row.Name)) return $"轴 {row.AxisNo} 名称为空";
  113. }
  114. var leafNos = new HashSet<int>(Rows.Where(r => !r.ToConfig().IsGrouped).Select(r => r.AxisNo));
  115. foreach (var row in Rows)
  116. {
  117. var cfg = row.ToConfig();
  118. var members = cfg.ParseMembers();
  119. bool wantGroup = cfg.Kind == MotionAxisKind.Combined
  120. || (cfg.Kind == MotionAxisKind.Virtual && !string.IsNullOrWhiteSpace(cfg.CombinedMembers));
  121. if (!wantGroup) continue;
  122. if (members.Count < 1) return $"{cfg.KindText} {row.Name} 至少需要 1 个从轴";
  123. foreach (var m in members)
  124. {
  125. if (m.AxisNo == row.AxisNo) return $"{row.Name} 不能把自身当作从轴";
  126. if (!leafNos.Contains(m.AxisNo))
  127. return $"{row.Name} 的从轴 {m.AxisNo} 必须是已有的实轴或未跟随的虚轴";
  128. }
  129. }
  130. return null;
  131. }
  132. }
  133. public class AxisEditRow : INotifyPropertyChanged
  134. {
  135. private int _axisNo;
  136. private string _name = "Axis";
  137. private MotionAxisKind _kind;
  138. private int _outputChannel;
  139. private string _combinedMembers = "";
  140. private double _velocity = 100;
  141. private bool _softLimitEnabled;
  142. private double _softLimitNeg;
  143. private double _softLimitPos;
  144. private double _maxVelocity;
  145. public int AxisNo { get => _axisNo; set { _axisNo = value; OnChanged(nameof(AxisNo)); } }
  146. public string Name { get => _name; set { _name = value; OnChanged(nameof(Name)); } }
  147. public MotionAxisKind Kind { get => _kind; set { _kind = value; OnChanged(nameof(Kind)); OnChanged(nameof(KindName)); } }
  148. public string KindName
  149. {
  150. get
  151. {
  152. switch (_kind)
  153. {
  154. case MotionAxisKind.Virtual: return "虚轴";
  155. case MotionAxisKind.Combined: return "组合轴";
  156. default: return "实轴";
  157. }
  158. }
  159. set
  160. {
  161. if (value == "虚轴") _kind = MotionAxisKind.Virtual;
  162. else if (value == "组合轴") _kind = MotionAxisKind.Combined;
  163. else _kind = MotionAxisKind.Physical;
  164. OnChanged(nameof(Kind));
  165. OnChanged(nameof(KindName));
  166. }
  167. }
  168. public int OutputChannel { get => _outputChannel; set { _outputChannel = value; OnChanged(nameof(OutputChannel)); } }
  169. public string CombinedMembers { get => _combinedMembers; set { _combinedMembers = value; OnChanged(nameof(CombinedMembers)); } }
  170. public double Velocity { get => _velocity; set { _velocity = value; OnChanged(nameof(Velocity)); } }
  171. public bool SoftLimitEnabled { get => _softLimitEnabled; set { _softLimitEnabled = value; OnChanged(nameof(SoftLimitEnabled)); } }
  172. public double SoftLimitNeg { get => _softLimitNeg; set { _softLimitNeg = value; OnChanged(nameof(SoftLimitNeg)); } }
  173. public double SoftLimitPos { get => _softLimitPos; set { _softLimitPos = value; OnChanged(nameof(SoftLimitPos)); } }
  174. public double MaxVelocity { get => _maxVelocity; set { _maxVelocity = value; OnChanged(nameof(MaxVelocity)); } }
  175. public event PropertyChangedEventHandler PropertyChanged;
  176. private void OnChanged(string name) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
  177. public static AxisEditRow FromConfig(MotionAxisConfig cfg)
  178. {
  179. cfg = cfg ?? new MotionAxisConfig();
  180. return new AxisEditRow
  181. {
  182. AxisNo = cfg.AxisNo,
  183. Name = cfg.Name,
  184. Kind = cfg.Kind,
  185. OutputChannel = cfg.OutputChannel,
  186. CombinedMembers = cfg.CombinedMembers,
  187. Velocity = cfg.Velocity,
  188. SoftLimitEnabled = cfg.SoftLimitEnabled,
  189. SoftLimitNeg = cfg.SoftLimitNeg,
  190. SoftLimitPos = cfg.SoftLimitPos,
  191. MaxVelocity = cfg.MaxVelocity
  192. };
  193. }
  194. public MotionAxisConfig ToConfig()
  195. {
  196. return new MotionAxisConfig
  197. {
  198. AxisNo = AxisNo,
  199. Name = Name?.Trim(),
  200. Kind = Kind,
  201. OutputChannel = OutputChannel,
  202. CombinedMembers = CombinedMembers?.Trim() ?? "",
  203. Velocity = Velocity,
  204. SoftLimitEnabled = SoftLimitEnabled,
  205. SoftLimitNeg = SoftLimitNeg,
  206. SoftLimitPos = SoftLimitPos,
  207. MaxVelocity = MaxVelocity
  208. };
  209. }
  210. }
  211. }