using HandyControl.Controls;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using TeamAAS.Motion.Interfaces;
using TeamAAS.Motion.Models;
using static TeamAAS.Motion.Enums.MotionEnums;
using static TeamAAS.Motion.Events.CardAxisEventArgs;
namespace TeamAAS.Motion.Motions
{
///
/// 仿真运动卡:无硬件环境下的全功能实现(联调/演示/单元测试用)。
/// 轴位置按指令速度渐变逼近目标(后台定时器推进),IO 点内存模拟。
/// 品牌适配器(雷赛/固高/IMC60G)实现 IMotionCard 时可直接参照本类的状态机结构。
///
public class SimulatedMotionCard : IMotionControl
{
private readonly MotionDeviceConfig _cfg;
private readonly int _maxAxisCount;
private readonly SimulatedAxis[] _axes;
private readonly bool [] SimulatedDI=new bool[100];
private readonly bool [] SimulatedDO=new bool[100];
private Timer _updateTimer;
private bool _isConnected = false;
private readonly object _lock = new object();
private const double UPDATE_INTERVAL_MS = 10; // 10ms 更新一次
public string CardName => _cfg.Name;
public bool IsConnected { get; private set; }
public int MaxAxisCount => _cfg.AxisCount;
public event EventHandler ConnectionStateChanged;
public event EventHandler ErrorOccurred;
public SimulatedMotionCard(MotionDeviceConfig cfg)
{
_cfg = cfg ?? new MotionDeviceConfig();
_axes= new SimulatedAxis[ MaxAxisCount ];
for ( int i = 0; i < MaxAxisCount; i++ )
_axes[ i ] = new SimulatedAxis(i);
for ( int i = 0; i < SimulatedDI.Length; i++ )
SimulatedDI[ i ] = ( i % 2 == 0 );
}
protected virtual void OnConnectionStateChanged(ConnectionStateChangedEventArgs e)
{
ConnectionStateChanged?.Invoke(this, e);
}
protected virtual void OnErrorOccurred(ErrorOccurredEventArgs e)
{
ErrorOccurred?.Invoke(this, e);
}
public bool Connect(string connectionString)
{
lock ( _lock )
{
if ( _isConnected ) return true;
// 模拟连接成功
_isConnected = true;
// 启动定时器更新轴状态
_updateTimer = new Timer(UpdateAxes, null, 0, ( int ) UPDATE_INTERVAL_MS);
OnConnectionStateChanged(new ConnectionStateChangedEventArgs(true));
return true;
}
}
public void Disconnect()
{
lock ( _lock )
{
if ( !_isConnected ) return;
_isConnected = false;
_updateTimer?.Dispose();
_updateTimer = null;
OnConnectionStateChanged(new ConnectionStateChangedEventArgs(false));
}
}
public IAxis GetAxis(int axisNo)
{
if ( axisNo < 0 || axisNo >= _maxAxisCount )
throw new ArgumentOutOfRangeException(nameof(axisNo), "轴号超出范围");
return _axes[ axisNo ];
}
public IList GetAxes()
{
return _axes.Cast().ToList();
}
public void MoveLinear(double[] targetPositions, double velocity)
{
if ( !_isConnected ) throw new InvalidOperationException("控制卡未连接");
if ( targetPositions.Length > _maxAxisCount )
throw new ArgumentException("目标位置数组长度超过轴数");
// 模拟:简单逐个轴移动(实际插补需协调)
for ( int i = 0; i < targetPositions.Length; i++ )
{
var axis = _axes[i];
if ( axis != null )
axis.MoveTo(targetPositions[ i ]);
}
}
public void MoveArc(double[] centerPoint, double[] targetPositions, double velocity)
{
if ( !_isConnected ) throw new InvalidOperationException("控制卡未连接");
if ( targetPositions.Length > _maxAxisCount )
throw new ArgumentException("目标位置数组长度超过轴数");
// 模拟:简单逐个轴移动(实际插补需协调)
for ( int i = 0; i < targetPositions.Length; i++ )
{
var axis = _axes[i];
if ( axis != null )
axis.MoveTo(targetPositions[ i ]);
}
}
public void EmergencyStop()
{
if ( !_isConnected ) return;
foreach ( var axis in _axes )
axis.Stop();
}
public bool ReadDI(int portIndex)
{
return SimulatedDI[ portIndex ];
}
public void WriteDO(int portIndex, bool value)
{
SimulatedDO[ portIndex ] = value;
}
// 定时更新所有轴
private void UpdateAxes(object state)
{
lock ( _lock )
{
if ( !_isConnected ) return;
double deltaTime = UPDATE_INTERVAL_MS / 1000.0;
foreach ( var axis in _axes )
axis.Update(deltaTime);
}
}
public void Dispose()
{
Disconnect();
}
}
public class SimulatedAxis : IAxis
{
private readonly object _lock = new object();
private AxisState _state = AxisState.Off;
private bool _isEnabled = false;
private bool _isMoving = false;
private bool _isHomed = false;
private double _commandPos = 0.0;
private double _actualPos = 0.0;
private double _actualVel = 0.0;
private double _targetPos = 0.0;
private double _velocity = 100.0; // 单位/秒
private double _accel = 500.0;
private double _decel = 500.0;
private double _jerk = 1000.0;
private double _jogVelocity = 0.0; // 0表示不点动
public int AxisNo { get; }
public string Name { get; set; }
public AxisState State => _state;
public bool IsEnabled => _isEnabled;
public bool IsMoving => _isMoving;
public bool IsHomed => _isHomed;
public double CommandPosition => _commandPos;
public double ActualPosition => _actualPos;
public double ActualVelocity => _actualVel;
public double Velocity { get => _velocity; set => _velocity = value; }
public double Acceleration { get => _accel; set => _accel = value; }
public double Deceleration { get => _decel; set => _decel = value; }
public double Jerk { get => _jerk; set => _jerk = value; }
public event EventHandler MotionStarted;
public event EventHandler MotionCompleted;
public event EventHandler StateChanged;
public event EventHandler ErrorOccurred;
public SimulatedAxis(int axisNo)
{
AxisNo = axisNo;
Name = $"Axis-{axisNo}";
}
// 由控制卡的定时器调用,更新轴状态(模拟位置变化)
internal void Update(double deltaTime)
{
lock ( _lock )
{
if ( !_isEnabled || _state == AxisState.Error || _state == AxisState.Off )
return;
// 如果处于点动状态(连续运动)
if ( _state == AxisState.ContinuousMotion && Math.Abs(_jogVelocity) > 0.0001 )
{
double delta = _jogVelocity * deltaTime;
_commandPos += delta;
_actualPos += delta; // 无误差模拟
_actualVel = _jogVelocity;
return;
}
// 如果处于点位运动状态(离散运动)
if ( _state == AxisState.DiscreteMotion )
{
double remaining = _targetPos - _actualPos;
double distance = Math.Abs(remaining);
if ( distance < 0.0001 )
{
// 到达目标
_actualPos = _targetPos;
_commandPos = _targetPos;
_actualVel = 0;
SetState(AxisState.StandStill);
_isMoving = false;
OnMotionCompleted(new AxisMotionEventArgs(AxisNo, _targetPos, _actualPos));
return;
}
// 简单匀速运动(为模拟可加入加减速,但此处保持简单)
double speed = Math.Sign(remaining) * _velocity;
double step = speed * deltaTime;
// 防止过冲
if ( Math.Abs(step) > Math.Abs(remaining) )
step = remaining;
_actualPos += step;
_commandPos = _actualPos; // 指令位置跟随
_actualVel = step / deltaTime;
}
}
}
// 启动点位运动(内部调用)
private void StartMoveTo(double target)
{
lock ( _lock )
{
if ( !_isEnabled )
{
OnError("轴未使能,无法运动");
return;
}
if ( _state == AxisState.Error )
{
OnError("轴处于错误状态");
return;
}
_targetPos = target;
SetState(AxisState.DiscreteMotion);
_isMoving = true;
_jogVelocity = 0; // 取消点动
OnMotionStarted(new AxisMotionEventArgs(AxisNo, target, _actualPos));
}
}
#region IAxis 方法实现
public void Enable()
{
lock ( _lock )
{
if ( _isEnabled ) return;
_isEnabled = true;
if ( _state == AxisState.Off ) SetState(AxisState.StandStill);
}
}
public void Disable()
{
lock ( _lock )
{
if ( !_isEnabled ) return;
// 停止运动
_isMoving = false;
_jogVelocity = 0;
SetState(AxisState.Off);
_isEnabled = false;
}
}
public void MoveTo(double position)
{
StartMoveTo(position);
}
public void MoveBy(double distance)
{
double target = _actualPos + distance;
StartMoveTo(target);
}
public void Jog(double velocity)
{
lock ( _lock )
{
if ( !_isEnabled )
{
OnError("轴未使能,无法点动");
return;
}
if ( _state == AxisState.Error )
{
OnError("轴处于错误状态");
return;
}
_jogVelocity = velocity;
SetState(AxisState.ContinuousMotion);
_isMoving = true;
_actualVel = velocity;
// 触发运动开始事件(带一个虚拟目标,因为点动无终点)
OnMotionStarted(new AxisMotionEventArgs(AxisNo, double.NaN, _actualPos));
}
}
public void Stop()
{
lock ( _lock )
{
if ( !_isMoving ) return;
_jogVelocity = 0;
if ( _state == AxisState.DiscreteMotion )
{
// 对于点位运动,停止后当前位置即为最终位置(但未到达目标,触发停止完成事件?可以触发完成但标记未到位)
// 简单处理:立即停止,触发完成事件并报告未到达目标
_actualVel = 0;
SetState(AxisState.StandStill);
_isMoving = false;
OnMotionCompleted(new AxisMotionEventArgs(AxisNo, _targetPos, _actualPos));
}
else if ( _state == AxisState.ContinuousMotion )
{
// 点动停止
_actualVel = 0;
SetState(AxisState.StandStill);
_isMoving = false;
OnMotionCompleted(new AxisMotionEventArgs(AxisNo, double.NaN, _actualPos));
}
}
}
public void Home(HomeMode mode = HomeMode.Default)
{
lock ( _lock )
{
if ( !_isEnabled )
{
OnError("轴未使能,无法回零");
return;
}
// 模拟回零:将当前位置设为0
_actualPos = 0;
_commandPos = 0;
_isHomed = true;
SetState(AxisState.StandStill);
// 触发回零完成事件?可以用MotionCompleted,但不设目标位置
OnMotionCompleted(new AxisMotionEventArgs(AxisNo, 0, 0));
}
}
public void SetPosition(double position)
{
lock ( _lock )
{
_actualPos = position;
_commandPos = position;
}
}
public void SetMotionParams(MotionParams parameters)
{
lock ( _lock )
{
if ( parameters != null )
{
_velocity = parameters.Velocity;
_accel = parameters.Acceleration;
_decel = parameters.Deceleration;
_jerk = parameters.Jerk;
}
}
}
#endregion
// 内部状态变更辅助
private void SetState(AxisState newState)
{
if ( _state != newState )
{
var old = _state;
_state = newState;
OnStateChanged(new AxisStateChangedEventArgs(AxisNo, old, newState));
}
}
// 事件触发辅助
protected virtual void OnMotionStarted(AxisMotionEventArgs e) => MotionStarted?.Invoke(this, e);
protected virtual void OnMotionCompleted(AxisMotionEventArgs e) => MotionCompleted?.Invoke(this, e);
protected virtual void OnStateChanged(AxisStateChangedEventArgs e) => StateChanged?.Invoke(this, e);
protected virtual void OnError(string message) => ErrorOccurred?.Invoke(this, new AxisErrorEventArgs(AxisNo, message));
}
/// 仿真 IO 卡(16进/16出,内存模拟,批量接口齐全)。
public class SimulatedIOCard : Global.Devices.IIOCard
{
private readonly MotionDeviceConfig _cfg;
private readonly bool[] _inputs;
private readonly bool[] _outputs;
public string Name => _cfg.Name;
public int InputCount => _inputs.Length;
public int OutputCount => _outputs.Length;
public bool IsConnected { get; private set; }
public SimulatedIOCard(MotionDeviceConfig cfg)
{
_cfg = cfg ?? new MotionDeviceConfig();
_inputs = new bool[ Math.Max(1, _cfg.InputCount) ];
_outputs = new bool[ Math.Max(1, _cfg.OutputCount) ];
}
public bool Open() { IsConnected = true; return true; }
public void Close() { IsConnected = false; }
public bool[] ReadAllInputs()
{
lock ( _inputs ) { return ( bool[] ) _inputs.Clone(); }
}
public bool[] ReadAllOutputs()
{
lock ( _outputs ) { return ( bool[] ) _outputs.Clone(); }
}
public bool WriteOutput(int index, bool value)
{
if ( index < 0 || index >= _outputs.Length ) return false;
lock ( _outputs ) { _outputs[ index ] = value; }
return true;
}
public bool WriteOutputs(bool[] values)
{
if ( values == null ) return false;
lock ( _outputs )
{
int n = Math.Min(values.Length, _outputs.Length);
for ( int i = 0; i < n; i++ ) _outputs[ i ] = values[ i ];
}
return true;
}
}
}