MotionAxisConfig.cs 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  1. using System;
  2. using System.Collections.Generic;
  3. using System.ComponentModel;
  4. using System.Globalization;
  5. using System.Linq;
  6. namespace TeamAAS.Motion.Models
  7. {
  8. /// <summary>逻辑轴类型。</summary>
  9. public enum MotionAxisKind
  10. {
  11. /// <summary>EtherCAT/脉冲实轴,绑定物理通道。</summary>
  12. [Description("实轴")]
  13. Physical = 0,
  14. /// <summary>虚轴:仅规划,不绑定输出。</summary>
  15. [Description("虚轴")]
  16. Virtual = 1,
  17. /// <summary>组合轴:一条逻辑轴同步驱动一个或多个从轴(主从跟随 / 双驱龙门)。</summary>
  18. [Description("组合轴")]
  19. Combined = 2
  20. }
  21. /// <summary>单条逻辑轴配置(持久化在 MotionDevices.json)。</summary>
  22. [Serializable]
  23. public class MotionAxisConfig
  24. {
  25. /// <summary>逻辑轴号,流程/调试用,范围 [0,63]。</summary>
  26. public int AxisNo { get; set; }
  27. public string Name { get; set; } = "Axis";
  28. public MotionAxisKind Kind { get; set; } = MotionAxisKind.Physical;
  29. /// <summary>实轴的 EtherCAT 输出通道;虚轴/组合轴忽略。</summary>
  30. public int OutputChannel { get; set; }
  31. /// <summary>
  32. /// 从轴列表。虚轴作为主轴时填写从轴号;正数为同向,负数为反向(例如 "1" 或 "1,-2")。
  33. /// </summary>
  34. public string CombinedMembers { get; set; } = "";
  35. public double Velocity { get; set; } = 100;
  36. public double Acceleration { get; set; } = 500;
  37. public double Deceleration { get; set; } = 500;
  38. public double Jerk { get; set; } = 1000;
  39. /// <summary>是否启用软限位。关闭时不钳制位置、不下发卡限位检查。</summary>
  40. public bool SoftLimitEnabled { get; set; }
  41. /// <summary>负向软限位(用户单位)。</summary>
  42. public double SoftLimitNeg { get; set; }
  43. /// <summary>正向软限位(用户单位)。</summary>
  44. public double SoftLimitPos { get; set; }
  45. /// <summary>最高速度。≤0 表示不限制。</summary>
  46. public double MaxVelocity { get; set; }
  47. public MotionAxisConfig Clone()
  48. {
  49. return new MotionAxisConfig
  50. {
  51. AxisNo = AxisNo,
  52. Name = Name,
  53. Kind = Kind,
  54. OutputChannel = OutputChannel,
  55. CombinedMembers = CombinedMembers,
  56. Velocity = Velocity,
  57. Acceleration = Acceleration,
  58. Deceleration = Deceleration,
  59. Jerk = Jerk,
  60. SoftLimitEnabled = SoftLimitEnabled,
  61. SoftLimitNeg = SoftLimitNeg,
  62. SoftLimitPos = SoftLimitPos,
  63. MaxVelocity = MaxVelocity
  64. };
  65. }
  66. public double ClampPosition(double position)
  67. {
  68. if (!SoftLimitEnabled) return position;
  69. double lo = Math.Min(SoftLimitNeg, SoftLimitPos);
  70. double hi = Math.Max(SoftLimitNeg, SoftLimitPos);
  71. if (position < lo) return lo;
  72. if (position > hi) return hi;
  73. return position;
  74. }
  75. public double ClampVelocity(double velocity)
  76. {
  77. if (MaxVelocity <= 0) return velocity;
  78. if (velocity > MaxVelocity) return MaxVelocity;
  79. if (velocity < -MaxVelocity) return -MaxVelocity;
  80. return velocity;
  81. }
  82. public double ClampJog(double velocity, double currentPosition)
  83. {
  84. double v = ClampVelocity(velocity);
  85. if (!SoftLimitEnabled || Math.Abs(v) < 1e-9) return v;
  86. double lo = Math.Min(SoftLimitNeg, SoftLimitPos);
  87. double hi = Math.Max(SoftLimitNeg, SoftLimitPos);
  88. if (v > 0 && currentPosition >= hi) return 0;
  89. if (v < 0 && currentPosition <= lo) return 0;
  90. return v;
  91. }
  92. public List<(int AxisNo, double Scale)> ParseMembers()
  93. {
  94. var list = new List<(int, double)>();
  95. if (string.IsNullOrWhiteSpace(CombinedMembers)) return list;
  96. foreach (var raw in CombinedMembers.Split(new[] { ',', ';', ' ', ',' }, StringSplitOptions.RemoveEmptyEntries))
  97. {
  98. var token = raw.Trim();
  99. if (token.Length == 0 || token == "-" || token == "+") continue;
  100. if (!int.TryParse(token, NumberStyles.Integer, CultureInfo.InvariantCulture, out var signed))
  101. continue;
  102. int no = Math.Abs(signed);
  103. double scale = token.StartsWith("-", StringComparison.Ordinal) ? -1 : 1;
  104. list.Add((no, scale));
  105. }
  106. return list;
  107. }
  108. /// <summary>组合轴,或虚轴填写了至少 1 个从轴时,按主从跟随运行。</summary>
  109. [Newtonsoft.Json.JsonIgnore]
  110. public bool IsGrouped
  111. {
  112. get
  113. {
  114. if (ParseMembers().Count < 1) return false;
  115. return Kind == MotionAxisKind.Combined || Kind == MotionAxisKind.Virtual;
  116. }
  117. }
  118. [Newtonsoft.Json.JsonIgnore]
  119. public string KindText
  120. {
  121. get
  122. {
  123. if (Kind == MotionAxisKind.Virtual && ParseMembers().Count >= 1)
  124. return "虚轴(主从)";
  125. switch (Kind)
  126. {
  127. case MotionAxisKind.Virtual: return "虚轴";
  128. case MotionAxisKind.Combined: return "组合轴";
  129. default: return "实轴";
  130. }
  131. }
  132. }
  133. }
  134. public static class MotionAxisCatalog
  135. {
  136. public static readonly string[] DefaultNames = { "X", "Y", "Z", "R" };
  137. public static List<MotionAxisConfig> CreateDefaults(int count)
  138. {
  139. count = Math.Max(1, count);
  140. var list = new List<MotionAxisConfig>(count);
  141. for (int i = 0; i < count; i++)
  142. {
  143. list.Add(new MotionAxisConfig
  144. {
  145. AxisNo = i,
  146. Name = i < DefaultNames.Length ? DefaultNames[i] : $"Axis-{i}",
  147. Kind = MotionAxisKind.Physical,
  148. OutputChannel = i
  149. });
  150. }
  151. return list;
  152. }
  153. public static List<MotionAxisConfig> GetEffective(MotionDeviceConfig cfg)
  154. {
  155. if (cfg?.Axes != null && cfg.Axes.Count > 0)
  156. {
  157. var byNo = new Dictionary<int, MotionAxisConfig>();
  158. foreach (var a in cfg.Axes)
  159. {
  160. if (a == null) continue;
  161. byNo[a.AxisNo] = a;
  162. }
  163. if (byNo.Count > 0)
  164. return byNo.Values.OrderBy(x => x.AxisNo).ToList();
  165. }
  166. return CreateDefaults(cfg?.AxisCount ?? 4);
  167. }
  168. }
  169. }