| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190 |
- using System;
- using System.Collections.Generic;
- using System.ComponentModel;
- using System.Globalization;
- using System.Linq;
- namespace TeamAAS.Motion.Models
- {
- /// <summary>逻辑轴类型。</summary>
- public enum MotionAxisKind
- {
- /// <summary>EtherCAT/脉冲实轴,绑定物理通道。</summary>
- [Description("实轴")]
- Physical = 0,
- /// <summary>虚轴:仅规划,不绑定输出。</summary>
- [Description("虚轴")]
- Virtual = 1,
- /// <summary>组合轴:一条逻辑轴同步驱动一个或多个从轴(主从跟随 / 双驱龙门)。</summary>
- [Description("组合轴")]
- Combined = 2
- }
- /// <summary>单条逻辑轴配置(持久化在 MotionDevices.json)。</summary>
- [Serializable]
- public class MotionAxisConfig
- {
- /// <summary>逻辑轴号,流程/调试用,范围 [0,63]。</summary>
- public int AxisNo { get; set; }
- public string Name { get; set; } = "Axis";
- public MotionAxisKind Kind { get; set; } = MotionAxisKind.Physical;
- /// <summary>实轴的 EtherCAT 输出通道;虚轴/组合轴忽略。</summary>
- public int OutputChannel { get; set; }
- /// <summary>
- /// 从轴列表。虚轴作为主轴时填写从轴号;正数为同向,负数为反向(例如 "1" 或 "1,-2")。
- /// </summary>
- public string CombinedMembers { get; set; } = "";
- public double Velocity { get; set; } = 100;
- public double Acceleration { get; set; } = 500;
- public double Deceleration { get; set; } = 500;
- public double Jerk { get; set; } = 1000;
- /// <summary>是否启用软限位。关闭时不钳制位置、不下发卡限位检查。</summary>
- public bool SoftLimitEnabled { get; set; }
- /// <summary>负向软限位(用户单位)。</summary>
- public double SoftLimitNeg { get; set; }
- /// <summary>正向软限位(用户单位)。</summary>
- public double SoftLimitPos { get; set; }
- /// <summary>最高速度。≤0 表示不限制。</summary>
- public double MaxVelocity { get; set; }
- public MotionAxisConfig Clone()
- {
- return new MotionAxisConfig
- {
- AxisNo = AxisNo,
- Name = Name,
- Kind = Kind,
- OutputChannel = OutputChannel,
- CombinedMembers = CombinedMembers,
- Velocity = Velocity,
- Acceleration = Acceleration,
- Deceleration = Deceleration,
- Jerk = Jerk,
- SoftLimitEnabled = SoftLimitEnabled,
- SoftLimitNeg = SoftLimitNeg,
- SoftLimitPos = SoftLimitPos,
- MaxVelocity = MaxVelocity
- };
- }
- public double ClampPosition(double position)
- {
- if (!SoftLimitEnabled) return position;
- double lo = Math.Min(SoftLimitNeg, SoftLimitPos);
- double hi = Math.Max(SoftLimitNeg, SoftLimitPos);
- if (position < lo) return lo;
- if (position > hi) return hi;
- return position;
- }
- public double ClampVelocity(double velocity)
- {
- if (MaxVelocity <= 0) return velocity;
- if (velocity > MaxVelocity) return MaxVelocity;
- if (velocity < -MaxVelocity) return -MaxVelocity;
- return velocity;
- }
- public double ClampJog(double velocity, double currentPosition)
- {
- double v = ClampVelocity(velocity);
- if (!SoftLimitEnabled || Math.Abs(v) < 1e-9) return v;
- double lo = Math.Min(SoftLimitNeg, SoftLimitPos);
- double hi = Math.Max(SoftLimitNeg, SoftLimitPos);
- if (v > 0 && currentPosition >= hi) return 0;
- if (v < 0 && currentPosition <= lo) return 0;
- return v;
- }
- public List<(int AxisNo, double Scale)> ParseMembers()
- {
- var list = new List<(int, double)>();
- if (string.IsNullOrWhiteSpace(CombinedMembers)) return list;
- foreach (var raw in CombinedMembers.Split(new[] { ',', ';', ' ', ',' }, StringSplitOptions.RemoveEmptyEntries))
- {
- var token = raw.Trim();
- if (token.Length == 0 || token == "-" || token == "+") continue;
- if (!int.TryParse(token, NumberStyles.Integer, CultureInfo.InvariantCulture, out var signed))
- continue;
- int no = Math.Abs(signed);
- double scale = token.StartsWith("-", StringComparison.Ordinal) ? -1 : 1;
- list.Add((no, scale));
- }
- return list;
- }
- /// <summary>组合轴,或虚轴填写了至少 1 个从轴时,按主从跟随运行。</summary>
- [Newtonsoft.Json.JsonIgnore]
- public bool IsGrouped
- {
- get
- {
- if (ParseMembers().Count < 1) return false;
- return Kind == MotionAxisKind.Combined || Kind == MotionAxisKind.Virtual;
- }
- }
- [Newtonsoft.Json.JsonIgnore]
- public string KindText
- {
- get
- {
- if (Kind == MotionAxisKind.Virtual && ParseMembers().Count >= 1)
- return "虚轴(主从)";
- switch (Kind)
- {
- case MotionAxisKind.Virtual: return "虚轴";
- case MotionAxisKind.Combined: return "组合轴";
- default: return "实轴";
- }
- }
- }
- }
- public static class MotionAxisCatalog
- {
- public static readonly string[] DefaultNames = { "X", "Y", "Z", "R" };
- public static List<MotionAxisConfig> CreateDefaults(int count)
- {
- count = Math.Max(1, count);
- var list = new List<MotionAxisConfig>(count);
- for (int i = 0; i < count; i++)
- {
- list.Add(new MotionAxisConfig
- {
- AxisNo = i,
- Name = i < DefaultNames.Length ? DefaultNames[i] : $"Axis-{i}",
- Kind = MotionAxisKind.Physical,
- OutputChannel = i
- });
- }
- return list;
- }
- public static List<MotionAxisConfig> GetEffective(MotionDeviceConfig cfg)
- {
- if (cfg?.Axes != null && cfg.Axes.Count > 0)
- {
- var byNo = new Dictionary<int, MotionAxisConfig>();
- foreach (var a in cfg.Axes)
- {
- if (a == null) continue;
- byNo[a.AxisNo] = a;
- }
- if (byNo.Count > 0)
- return byNo.Values.OrderBy(x => x.AxisNo).ToList();
- }
- return CreateDefaults(cfg?.AxisCount ?? 4);
- }
- }
- }
|