using System;
using System.Collections.Generic;
using System.Linq;
using TeamAAS.Global.Devices;
using TeamAAS.Motion.Models;
namespace TeamAAS.Motion.Motions
{
///
/// 主从跟随:主轴(虚轴)有独立规划位置;从轴只跟随主轴的相对位移。
/// 主轴从 0→10,从轴当前 1 则走到 11。从轴可被多条主轴共用,互不影响各自主轴读数。
///
public class CombinedMotionAxis : IAxis
{
private readonly IMotionCard _card;
private readonly MotionAxisConfig _cfg;
private readonly IAxis _master;
private readonly Func _resolve;
public CombinedMotionAxis(IMotionCard card, MotionAxisConfig cfg, IAxis master, Func 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 MotionStarted;
public event EventHandler MotionCompleted;
public event EventHandler StateChanged;
public event EventHandler 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() { }
}
}
}