| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289 |
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using TeamAAS.Global.Devices;
- using TeamAAS.Motion.Models;
- namespace TeamAAS.Motion.Motions
- {
- /// <summary>
- /// 主从跟随:主轴(虚轴)有独立规划位置;从轴只跟随主轴的相对位移。
- /// 主轴从 0→10,从轴当前 1 则走到 11。从轴可被多条主轴共用,互不影响各自主轴读数。
- /// </summary>
- public class CombinedMotionAxis : IAxis
- {
- private readonly IMotionCard _card;
- private readonly MotionAxisConfig _cfg;
- private readonly IAxis _master;
- private readonly Func<int, IAxis> _resolve;
- public CombinedMotionAxis(IMotionCard card, MotionAxisConfig cfg, IAxis master, Func<int, IAxis> resolve)
- {
- _card = card;
- _cfg = cfg ?? new MotionAxisConfig();
- _master = master;
- _resolve = resolve;
- AxisNo = _cfg.AxisNo;
- Name = string.IsNullOrWhiteSpace(_cfg.Name) ? $"Master-{AxisNo}" : _cfg.Name;
- Velocity = _cfg.Velocity;
- Acceleration = _cfg.Acceleration;
- Deceleration = _cfg.Deceleration;
- Jerk = _cfg.Jerk;
- if (_master != null)
- {
- _master.Name = Name;
- _master.Velocity = Velocity;
- _master.Acceleration = Acceleration;
- _master.Deceleration = Deceleration;
- _master.Jerk = Jerk;
- }
- }
- public int AxisNo { get; }
- public string Name { get; set; }
- public double Velocity { get; set; }
- public double Acceleration { get; set; }
- public double Deceleration { get; set; }
- public double Jerk { get; set; }
- public event EventHandler<AxisMotionEventArgs> MotionStarted;
- public event EventHandler<AxisMotionEventArgs> MotionCompleted;
- public event EventHandler<AxisStateChangedEventArgs> StateChanged;
- public event EventHandler<AxisErrorEventArgs> ErrorOccurred;
- public AxisState State => _master != null ? _master.State : AxisState.Off;
- public bool IsEnabled => _master != null && _master.IsEnabled;
- public bool IsMoving => _master != null && _master.IsMoving;
- public bool IsHomed => _master != null && _master.IsHomed;
- public double CommandPosition => _master != null ? _master.CommandPosition : 0;
- public double ActualPosition => _master != null ? _master.CommandPosition : 0;
- public double ActualVelocity => _master != null ? _master.ActualVelocity : 0;
- private IEnumerable<(IAxis Axis, double Scale)> Slaves()
- {
- foreach (var m in _cfg.ParseMembers())
- {
- if (m.AxisNo == AxisNo) continue;
- var a = _resolve(m.AxisNo);
- if (a != null && !ReferenceEquals(a, _master) && !(a is CombinedMotionAxis))
- yield return (a, m.Scale);
- }
- }
- private void ApplyParams(IAxis axis)
- {
- if (axis == null) return;
- axis.Velocity = Velocity;
- axis.Acceleration = Acceleration;
- axis.Deceleration = Deceleration;
- axis.Jerk = Jerk;
- }
- private IMasterFollowHost FollowHost => _card as IMasterFollowHost;
- private IDisposable SoloFollow()
- {
- var host = FollowHost;
- var slaves = Slaves().Select(s => s.Axis).ToList();
- var scope = host?.SuspendFollowRelease();
- host?.BeginSoloMasterFollow(AxisNo, slaves);
- return scope ?? EmptyScope.Instance;
- }
- public void Enable()
- {
- if (_card is IMotionSafetyHost safety && !safety.AllowEnable(out var reason))
- {
- OnError(reason);
- return;
- }
- DriveMasterEnable(true);
- }
- public void Disable()
- {
- FollowHost?.ReleaseMasterFollow();
- DriveMasterEnable(false);
- }
- private MotionAxisConfig CfgOf(int axisNo)
- {
- if (_card is InovanceMotionCard imc) return imc.GetAxisConfig(axisNo);
- if (_card is SimulatedMotionCard sim) return sim.GetAxisConfig(axisNo);
- return axisNo == AxisNo ? _cfg : new MotionAxisConfig { AxisNo = axisNo };
- }
- public void MoveTo(double position)
- {
- if (!_card.IsConnected) { OnError("运动卡未连接"); return; }
- if (_card is IMotionSafetyHost safety && !safety.AllowMotion(out var reason)) { OnError(reason); return; }
- if (_master == null) { OnError("主轴无效"); return; }
- var masterCfg = CfgOf(AxisNo);
- position = masterCfg.ClampPosition(position);
- Velocity = Math.Abs(masterCfg.ClampVelocity(Velocity));
- double delta = position - _master.CommandPosition;
- using (SoloFollow())
- {
- ApplyParams(_master);
- if (!DriveMasterMoveAbs(position)) { OnError("主轴定位失败"); return; }
- FollowRelative(delta);
- }
- MotionStarted?.Invoke(this, new AxisMotionEventArgs(AxisNo, position, ActualPosition));
- }
- public void MoveBy(double distance)
- {
- if (!_card.IsConnected) { OnError("运动卡未连接"); return; }
- if (_card is IMotionSafetyHost safety && !safety.AllowMotion(out var reason)) { OnError(reason); return; }
- if (_master == null) { OnError("主轴无效"); return; }
- var masterCfg = CfgOf(AxisNo);
- double target = masterCfg.ClampPosition(_master.CommandPosition + distance);
- Velocity = Math.Abs(masterCfg.ClampVelocity(Velocity));
- double delta = target - _master.CommandPosition;
- using (SoloFollow())
- {
- ApplyParams(_master);
- if (!DriveMasterMoveRel(delta)) { OnError("主轴相对定位失败"); return; }
- FollowRelative(delta);
- }
- MotionStarted?.Invoke(this, new AxisMotionEventArgs(AxisNo, CommandPosition, ActualPosition));
- }
- public void Jog(double velocity)
- {
- if (!_card.IsConnected) { OnError("运动卡未连接"); return; }
- if (_card is IMotionSafetyHost safety && !safety.AllowMotion(out var reason)) { OnError(reason); return; }
- if (_master == null) { OnError("主轴无效"); return; }
- velocity = CfgOf(AxisNo).ClampJog(velocity, _master.CommandPosition);
- using (SoloFollow())
- {
- ApplyParams(_master);
- if (Math.Abs(velocity) < 1e-9)
- {
- DriveMasterStop();
- return;
- }
- if (!DriveMasterJog(velocity)) { OnError("主轴点动失败"); return; }
- foreach (var s in Slaves())
- {
- if (!s.Axis.IsEnabled) { OnError($"从轴 {s.Axis.Name} 未使能"); return; }
- ApplyParams(s.Axis);
- double sv = CfgOf(s.Axis.AxisNo).ClampJog(velocity * s.Scale, s.Axis.CommandPosition);
- s.Axis.Jog(sv);
- }
- }
- MotionStarted?.Invoke(this, new AxisMotionEventArgs(AxisNo, double.NaN, ActualPosition));
- }
- public void Stop()
- {
- DriveMasterStop();
- FollowHost?.ReleaseMasterFollow();
- MotionCompleted?.Invoke(this, new AxisMotionEventArgs(AxisNo, CommandPosition, ActualPosition));
- }
- public void Home(HomeMode mode = HomeMode.Default)
- {
- if (!_card.IsConnected) { OnError("运动卡未连接"); return; }
- if (_card is IMotionSafetyHost safety && !safety.AllowMotion(out var reason)) { OnError(reason); return; }
- if (_master == null) { OnError("主轴无效"); return; }
- double delta = -_master.CommandPosition;
- using (SoloFollow())
- {
- ApplyParams(_master);
- DriveMasterHome(mode);
- FollowRelative(delta);
- }
- MotionStarted?.Invoke(this, new AxisMotionEventArgs(AxisNo, 0, ActualPosition));
- }
- public void SetPosition(double position)
- {
- if (_master == null) return;
- if (_card is InovanceMotionCard imc)
- imc.SetCurrentPosition(AxisNo, position);
- else
- _master.SetPosition(position);
- }
- public void SetMotionParams(MotionParams parameters)
- {
- if (parameters == null) return;
- Velocity = parameters.Velocity;
- Acceleration = parameters.Acceleration;
- Deceleration = parameters.Deceleration;
- Jerk = parameters.Jerk;
- ApplyParams(_master);
- }
- private void FollowRelative(double masterDelta)
- {
- foreach (var s in Slaves())
- {
- if (!s.Axis.IsEnabled) { OnError($"从轴 {s.Axis.Name} 未使能"); return; }
- ApplyParams(s.Axis);
- double slaveDelta = masterDelta * s.Scale;
- double target = CfgOf(s.Axis.AxisNo).ClampPosition(s.Axis.CommandPosition + slaveDelta);
- s.Axis.MoveTo(target);
- }
- }
- private bool DriveMasterEnable(bool on)
- {
- if (_master == null) return false;
- if (_card is InovanceMotionCard imc)
- return imc.HardwareSetEnable(AxisNo, on);
- if (on) _master.Enable(); else _master.Disable();
- return true;
- }
- private bool DriveMasterMoveAbs(double position)
- {
- if (_card is InovanceMotionCard imc)
- return imc.HardwareMoveAbsolute(AxisNo, position, Velocity);
- _master.MoveTo(position);
- return true;
- }
- private bool DriveMasterMoveRel(double distance)
- {
- if (_card is InovanceMotionCard imc)
- return imc.HardwareMoveRelative(AxisNo, distance, Velocity);
- _master.MoveBy(distance);
- return true;
- }
- private bool DriveMasterJog(double velocity)
- {
- if (_card is InovanceMotionCard imc)
- return imc.HardwareJog(AxisNo, velocity);
- _master.Jog(velocity);
- return true;
- }
- private void DriveMasterStop()
- {
- if (_card is InovanceMotionCard imc)
- imc.HardwareStop(AxisNo);
- else
- _master?.Stop();
- }
- private void DriveMasterHome(HomeMode mode)
- {
- if (_card is InovanceMotionCard imc)
- imc.HardwareHome(AxisNo, (int)mode);
- else
- _master?.Home(mode);
- }
- private void OnError(string message)
- => ErrorOccurred?.Invoke(this, new AxisErrorEventArgs(AxisNo, message));
- private sealed class EmptyScope : IDisposable
- {
- public static readonly EmptyScope Instance = new EmptyScope();
- public void Dispose() { }
- }
- }
- }
|