using System; using System.Collections.Generic; using System.Threading; namespace TeamAAS.Motion { /// /// 仿真运动卡:无硬件环境下的全功能实现(联调/演示/单元测试用)。 /// 轴位置按指令速度渐变逼近目标(后台定时器推进),IO 点内存模拟。 /// 品牌适配器(雷赛/固高/IMC60G)实现 IMotionCard 时可直接参照本类的状态机结构。 /// public class SimulatedMotionCard : TeamAAS.Global.Devices.IMotionCard { private readonly MotionDeviceConfig _cfg; private readonly object _sync = new object(); private readonly double[] _pos; private readonly double[] _target; private readonly double[] _vel; private readonly bool[] _moving; private readonly bool[] _homed; private readonly bool[] _enabled; public string Name => _cfg.Name; public int AxisCount => _cfg.AxisCount; public bool IsConnected { get; private set; } public SimulatedMotionCard(MotionDeviceConfig cfg) { _cfg = cfg ?? new MotionDeviceConfig(); int n = Math.Max(1, _cfg.AxisCount); _pos = new double[n]; _target = new double[n]; _vel = new double[n]; _moving = new bool[n]; _homed = new bool[n]; _enabled = new bool[n]; // 后台轮询推进仿真轴运动(50ms 周期) var t = new System.Threading.Timer(Tick, null, 50, 50); } private void Tick(object state) { lock (_sync) { for (int i = 0; i < _pos.Length; i++) { if (!_moving[i]) continue; double step = _vel[i] * 0.05; // 50ms 周期 double diff = _target[i] - _pos[i]; if (Math.Abs(diff) <= step) { _pos[i] = _target[i]; _moving[i] = false; } else _pos[i] += Math.Sign(diff) * step; } } } public bool Open() { IsConnected = true; return true; } public void Close() { IsConnected = false; } public bool SetEnable(int axis, bool enable) { ApplyAxis(ref axis); lock (_sync) { _enabled[axis] = enable; } return true; } public bool Home(int axis, int mode = 0) { ApplyAxis(ref axis); lock (_sync) { _pos[axis] = 0; _target[axis] = 0; _moving[axis] = false; _homed[axis] = true; } return true; } public bool IsHomed(int axis) { lock (_sync) { return _homed[ClampAxis(axis)]; } } public bool MoveAbsolute(int axis, double position, double velocity) { ApplyAxis(ref axis); lock (_sync) { _target[axis] = position; _vel[axis] = Math.Abs(velocity) <= 0 ? 1 : Math.Abs(velocity); _moving[axis] = true; } return true; } public bool MoveRelative(int axis, double distance, double velocity) { lock (_sync) { return MoveAbsolute(axis, _pos[ClampAxis(axis)] + distance, velocity); } } public bool JogStart(int axis, int direction, double velocity) { ApplyAxis(ref axis); lock (_sync) { _target[axis] = direction >= 0 ? double.MaxValue : double.MinValue; _vel[axis] = Math.Abs(velocity) <= 0 ? 1 : Math.Abs(velocity); _moving[axis] = true; } return true; } public bool JogStop(int axis) { lock (_sync) { int i = ClampAxis(axis); _target[i] = _pos[i]; _moving[i] = false; } return true; } public bool Stop(int axis = -1) { lock (_sync) { for (int i = 0; i < _pos.Length; i++) { if (axis >= 0 && i != axis) continue; _target[i] = _pos[i]; _moving[i] = false; } } return true; } public TeamAAS.Global.Devices.AxisStatus GetAxisStatus(int axis) { int i = ClampAxis(axis); lock (_sync) { return new TeamAAS.Global.Devices.AxisStatus { Index = i, Name = $"轴{i}", Position = _pos[i], Velocity = _moving[i] ? _vel[i] : 0, IsMoving = _moving[i], IsHomed = _homed[i], IsEnabled = _enabled[i], }; } } public List GetAllAxisStatus() { var list = new List(); for (int i = 0; i < AxisCount; i++) list.Add(GetAxisStatus(i)); return list; } private int ClampAxis(int axis) => Math.Max(0, Math.Min(axis < 0 ? 0 : axis, _pos.Length - 1)); private void ApplyAxis(ref int axis) { if (axis < 0) axis = 0; if (axis >= _pos.Length) axis = _pos.Length - 1; } } /// 仿真 IO 卡(16进/16出,内存模拟,批量接口齐全)。 public class SimulatedIOCard : TeamAAS.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; } } }