CombinedMotionAxis.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using TeamAAS.Global.Devices;
  5. using TeamAAS.Motion.Models;
  6. namespace TeamAAS.Motion.Motions
  7. {
  8. /// <summary>
  9. /// 主从跟随:主轴(虚轴)有独立规划位置;从轴只跟随主轴的相对位移。
  10. /// 主轴从 0→10,从轴当前 1 则走到 11。从轴可被多条主轴共用,互不影响各自主轴读数。
  11. /// </summary>
  12. public class CombinedMotionAxis : IAxis
  13. {
  14. private readonly IMotionCard _card;
  15. private readonly MotionAxisConfig _cfg;
  16. private readonly IAxis _master;
  17. private readonly Func<int, IAxis> _resolve;
  18. public CombinedMotionAxis(IMotionCard card, MotionAxisConfig cfg, IAxis master, Func<int, IAxis> resolve)
  19. {
  20. _card = card;
  21. _cfg = cfg ?? new MotionAxisConfig();
  22. _master = master;
  23. _resolve = resolve;
  24. AxisNo = _cfg.AxisNo;
  25. Name = string.IsNullOrWhiteSpace(_cfg.Name) ? $"Master-{AxisNo}" : _cfg.Name;
  26. Velocity = _cfg.Velocity;
  27. Acceleration = _cfg.Acceleration;
  28. Deceleration = _cfg.Deceleration;
  29. Jerk = _cfg.Jerk;
  30. if (_master != null)
  31. {
  32. _master.Name = Name;
  33. _master.Velocity = Velocity;
  34. _master.Acceleration = Acceleration;
  35. _master.Deceleration = Deceleration;
  36. _master.Jerk = Jerk;
  37. }
  38. }
  39. public int AxisNo { get; }
  40. public string Name { get; set; }
  41. public double Velocity { get; set; }
  42. public double Acceleration { get; set; }
  43. public double Deceleration { get; set; }
  44. public double Jerk { get; set; }
  45. public event EventHandler<AxisMotionEventArgs> MotionStarted;
  46. public event EventHandler<AxisMotionEventArgs> MotionCompleted;
  47. public event EventHandler<AxisStateChangedEventArgs> StateChanged;
  48. public event EventHandler<AxisErrorEventArgs> ErrorOccurred;
  49. public AxisState State => _master != null ? _master.State : AxisState.Off;
  50. public bool IsEnabled => _master != null && _master.IsEnabled;
  51. public bool IsMoving => _master != null && _master.IsMoving;
  52. public bool IsHomed => _master != null && _master.IsHomed;
  53. public double CommandPosition => _master != null ? _master.CommandPosition : 0;
  54. public double ActualPosition => _master != null ? _master.CommandPosition : 0;
  55. public double ActualVelocity => _master != null ? _master.ActualVelocity : 0;
  56. private IEnumerable<(IAxis Axis, double Scale)> Slaves()
  57. {
  58. foreach (var m in _cfg.ParseMembers())
  59. {
  60. if (m.AxisNo == AxisNo) continue;
  61. var a = _resolve(m.AxisNo);
  62. if (a != null && !ReferenceEquals(a, _master) && !(a is CombinedMotionAxis))
  63. yield return (a, m.Scale);
  64. }
  65. }
  66. private void ApplyParams(IAxis axis)
  67. {
  68. if (axis == null) return;
  69. axis.Velocity = Velocity;
  70. axis.Acceleration = Acceleration;
  71. axis.Deceleration = Deceleration;
  72. axis.Jerk = Jerk;
  73. }
  74. private IMasterFollowHost FollowHost => _card as IMasterFollowHost;
  75. private IDisposable SoloFollow()
  76. {
  77. var host = FollowHost;
  78. var slaves = Slaves().Select(s => s.Axis).ToList();
  79. var scope = host?.SuspendFollowRelease();
  80. host?.BeginSoloMasterFollow(AxisNo, slaves);
  81. return scope ?? EmptyScope.Instance;
  82. }
  83. public void Enable()
  84. {
  85. if (_card is IMotionSafetyHost safety && !safety.AllowEnable(out var reason))
  86. {
  87. OnError(reason);
  88. return;
  89. }
  90. DriveMasterEnable(true);
  91. }
  92. public void Disable()
  93. {
  94. FollowHost?.ReleaseMasterFollow();
  95. DriveMasterEnable(false);
  96. }
  97. private MotionAxisConfig CfgOf(int axisNo)
  98. {
  99. if (_card is InovanceMotionCard imc) return imc.GetAxisConfig(axisNo);
  100. if (_card is SimulatedMotionCard sim) return sim.GetAxisConfig(axisNo);
  101. return axisNo == AxisNo ? _cfg : new MotionAxisConfig { AxisNo = axisNo };
  102. }
  103. public void MoveTo(double position)
  104. {
  105. if (!_card.IsConnected) { OnError("运动卡未连接"); return; }
  106. if (_card is IMotionSafetyHost safety && !safety.AllowMotion(out var reason)) { OnError(reason); return; }
  107. if (_master == null) { OnError("主轴无效"); return; }
  108. var masterCfg = CfgOf(AxisNo);
  109. position = masterCfg.ClampPosition(position);
  110. Velocity = Math.Abs(masterCfg.ClampVelocity(Velocity));
  111. double delta = position - _master.CommandPosition;
  112. using (SoloFollow())
  113. {
  114. ApplyParams(_master);
  115. if (!DriveMasterMoveAbs(position)) { OnError("主轴定位失败"); return; }
  116. FollowRelative(delta);
  117. }
  118. MotionStarted?.Invoke(this, new AxisMotionEventArgs(AxisNo, position, ActualPosition));
  119. }
  120. public void MoveBy(double distance)
  121. {
  122. if (!_card.IsConnected) { OnError("运动卡未连接"); return; }
  123. if (_card is IMotionSafetyHost safety && !safety.AllowMotion(out var reason)) { OnError(reason); return; }
  124. if (_master == null) { OnError("主轴无效"); return; }
  125. var masterCfg = CfgOf(AxisNo);
  126. double target = masterCfg.ClampPosition(_master.CommandPosition + distance);
  127. Velocity = Math.Abs(masterCfg.ClampVelocity(Velocity));
  128. double delta = target - _master.CommandPosition;
  129. using (SoloFollow())
  130. {
  131. ApplyParams(_master);
  132. if (!DriveMasterMoveRel(delta)) { OnError("主轴相对定位失败"); return; }
  133. FollowRelative(delta);
  134. }
  135. MotionStarted?.Invoke(this, new AxisMotionEventArgs(AxisNo, CommandPosition, ActualPosition));
  136. }
  137. public void Jog(double velocity)
  138. {
  139. if (!_card.IsConnected) { OnError("运动卡未连接"); return; }
  140. if (_card is IMotionSafetyHost safety && !safety.AllowMotion(out var reason)) { OnError(reason); return; }
  141. if (_master == null) { OnError("主轴无效"); return; }
  142. velocity = CfgOf(AxisNo).ClampJog(velocity, _master.CommandPosition);
  143. using (SoloFollow())
  144. {
  145. ApplyParams(_master);
  146. if (Math.Abs(velocity) < 1e-9)
  147. {
  148. DriveMasterStop();
  149. return;
  150. }
  151. if (!DriveMasterJog(velocity)) { OnError("主轴点动失败"); return; }
  152. foreach (var s in Slaves())
  153. {
  154. if (!s.Axis.IsEnabled) { OnError($"从轴 {s.Axis.Name} 未使能"); return; }
  155. ApplyParams(s.Axis);
  156. double sv = CfgOf(s.Axis.AxisNo).ClampJog(velocity * s.Scale, s.Axis.CommandPosition);
  157. s.Axis.Jog(sv);
  158. }
  159. }
  160. MotionStarted?.Invoke(this, new AxisMotionEventArgs(AxisNo, double.NaN, ActualPosition));
  161. }
  162. public void Stop()
  163. {
  164. DriveMasterStop();
  165. FollowHost?.ReleaseMasterFollow();
  166. MotionCompleted?.Invoke(this, new AxisMotionEventArgs(AxisNo, CommandPosition, ActualPosition));
  167. }
  168. public void Home(HomeMode mode = HomeMode.Default)
  169. {
  170. if (!_card.IsConnected) { OnError("运动卡未连接"); return; }
  171. if (_card is IMotionSafetyHost safety && !safety.AllowMotion(out var reason)) { OnError(reason); return; }
  172. if (_master == null) { OnError("主轴无效"); return; }
  173. double delta = -_master.CommandPosition;
  174. using (SoloFollow())
  175. {
  176. ApplyParams(_master);
  177. DriveMasterHome(mode);
  178. FollowRelative(delta);
  179. }
  180. MotionStarted?.Invoke(this, new AxisMotionEventArgs(AxisNo, 0, ActualPosition));
  181. }
  182. public void SetPosition(double position)
  183. {
  184. if (_master == null) return;
  185. if (_card is InovanceMotionCard imc)
  186. imc.SetCurrentPosition(AxisNo, position);
  187. else
  188. _master.SetPosition(position);
  189. }
  190. public void SetMotionParams(MotionParams parameters)
  191. {
  192. if (parameters == null) return;
  193. Velocity = parameters.Velocity;
  194. Acceleration = parameters.Acceleration;
  195. Deceleration = parameters.Deceleration;
  196. Jerk = parameters.Jerk;
  197. ApplyParams(_master);
  198. }
  199. private void FollowRelative(double masterDelta)
  200. {
  201. foreach (var s in Slaves())
  202. {
  203. if (!s.Axis.IsEnabled) { OnError($"从轴 {s.Axis.Name} 未使能"); return; }
  204. ApplyParams(s.Axis);
  205. double slaveDelta = masterDelta * s.Scale;
  206. double target = CfgOf(s.Axis.AxisNo).ClampPosition(s.Axis.CommandPosition + slaveDelta);
  207. s.Axis.MoveTo(target);
  208. }
  209. }
  210. private bool DriveMasterEnable(bool on)
  211. {
  212. if (_master == null) return false;
  213. if (_card is InovanceMotionCard imc)
  214. return imc.HardwareSetEnable(AxisNo, on);
  215. if (on) _master.Enable(); else _master.Disable();
  216. return true;
  217. }
  218. private bool DriveMasterMoveAbs(double position)
  219. {
  220. if (_card is InovanceMotionCard imc)
  221. return imc.HardwareMoveAbsolute(AxisNo, position, Velocity);
  222. _master.MoveTo(position);
  223. return true;
  224. }
  225. private bool DriveMasterMoveRel(double distance)
  226. {
  227. if (_card is InovanceMotionCard imc)
  228. return imc.HardwareMoveRelative(AxisNo, distance, Velocity);
  229. _master.MoveBy(distance);
  230. return true;
  231. }
  232. private bool DriveMasterJog(double velocity)
  233. {
  234. if (_card is InovanceMotionCard imc)
  235. return imc.HardwareJog(AxisNo, velocity);
  236. _master.Jog(velocity);
  237. return true;
  238. }
  239. private void DriveMasterStop()
  240. {
  241. if (_card is InovanceMotionCard imc)
  242. imc.HardwareStop(AxisNo);
  243. else
  244. _master?.Stop();
  245. }
  246. private void DriveMasterHome(HomeMode mode)
  247. {
  248. if (_card is InovanceMotionCard imc)
  249. imc.HardwareHome(AxisNo, (int)mode);
  250. else
  251. _master?.Home(mode);
  252. }
  253. private void OnError(string message)
  254. => ErrorOccurred?.Invoke(this, new AxisErrorEventArgs(AxisNo, message));
  255. private sealed class EmptyScope : IDisposable
  256. {
  257. public static readonly EmptyScope Instance = new EmptyScope();
  258. public void Dispose() { }
  259. }
  260. }
  261. }