249 lines
9.7 KiB
C#
249 lines
9.7 KiB
C#
using MainShell.Common;
|
|
using MainShell.Hardware;
|
|
using MainShell.Log;
|
|
using MainShell.Parameter;
|
|
using MaxwellFramework.Core.Attributes;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using static MainShell.ParaSetting.Model.SpeedSetting;
|
|
|
|
namespace MainShell.Motion
|
|
{
|
|
public enum AxisSpeedProfile
|
|
{
|
|
Low,
|
|
Medium,
|
|
High,
|
|
Custom
|
|
}
|
|
|
|
public sealed class AxisMotionProfile
|
|
{
|
|
public AxisMotionProfile(double velocity, double acceleration, double deceleration, double jerk)
|
|
{
|
|
Velocity = velocity;
|
|
Acceleration = acceleration;
|
|
Deceleration = deceleration;
|
|
Jerk = jerk;
|
|
}
|
|
|
|
public double Velocity { get; }
|
|
public double Acceleration { get; }
|
|
public double Deceleration { get; }
|
|
public double Jerk { get; }
|
|
}
|
|
|
|
[Singleton]
|
|
public class AxisSpeedManager
|
|
{
|
|
private readonly GlobalParameterContext _globalParam;
|
|
private readonly HardwareManager _hardware;
|
|
|
|
public AxisSpeedManager(GlobalParameterContext globalParam, HardwareManager hardware)
|
|
{
|
|
_globalParam = globalParam ?? throw new ArgumentNullException(nameof(globalParam));
|
|
_hardware = hardware ?? throw new ArgumentNullException(nameof(hardware));
|
|
}
|
|
|
|
public void SetAxisSpeed(string axisName, double speed)
|
|
{
|
|
var speedItem = GetRequiredSpeedTypeItem(axisName);
|
|
var profile = CreateCustomProfile(speedItem, speed);
|
|
ApplyAxisMotionProfile(axisName, profile, AxisSpeedProfile.Custom, null);
|
|
}
|
|
|
|
public void SetAxisSpeed(string axisName, AxisSpeedProfile profile)
|
|
{
|
|
var speedItem = GetRequiredSpeedTypeItem(axisName);
|
|
var motionProfile = CreateProfile(speedItem, profile);
|
|
ApplyAxisMotionProfile(axisName, motionProfile, profile, speedItem);
|
|
}
|
|
|
|
public void ApplySpeedSettings()
|
|
{
|
|
var speedItems = _globalParam?.SpeedParaSysSetting?.SpeedTypeItemCollection?.SpeedTypeItemList;
|
|
if (speedItems == null || speedItems.Count == 0)
|
|
{
|
|
"Axis speed settings skipped because no speed configuration is available.".LogInfo();
|
|
return;
|
|
}
|
|
|
|
var currentProfile = MapCurrentSpeedTypeText(_globalParam.SpeedParaSysSetting.CurrentSpeedType.ToString());
|
|
foreach (var speedItem in speedItems)
|
|
{
|
|
if (speedItem == null || string.IsNullOrWhiteSpace(speedItem.AxisName))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
var profile = CreateProfile(speedItem, currentProfile);
|
|
ApplyAxisMotionProfile(speedItem.AxisName, profile, currentProfile, speedItem);
|
|
}
|
|
}
|
|
|
|
private void ApplyAxisMotionProfile(string axisName, AxisMotionProfile profile, AxisSpeedProfile profileType, SpeedTypeItem speedItem)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(axisName))
|
|
{
|
|
throw new ArgumentNullException(nameof(axisName));
|
|
}
|
|
|
|
var axis = _hardware.GetAxisByName(axisName);
|
|
if (axis == null)
|
|
{
|
|
throw new ArgumentException(string.Format("Axis with name {0} not found.", axisName), nameof(axisName));
|
|
}
|
|
|
|
ValidateMotionProfile(profile, axisName);
|
|
|
|
axis.Param.Velocity = profile.Velocity;
|
|
axis.Param.Accelerate = profile.Acceleration;
|
|
axis.Param.Decelerate = profile.Deceleration;
|
|
axis.Param.Jerk = profile.Jerk;
|
|
|
|
try
|
|
{
|
|
axis.IssueParam();
|
|
LogProfileApplied(axisName, profile, profileType, speedItem);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
string.Format("Axis:{0} speed profile apply failed. Profile:{1} Error:{2}", axisName, profileType, ex.Message).LogSysError();
|
|
throw;
|
|
}
|
|
}
|
|
|
|
private SpeedTypeItem GetRequiredSpeedTypeItem(string axisName)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(axisName))
|
|
{
|
|
throw new ArgumentNullException(nameof(axisName));
|
|
}
|
|
|
|
var speedItem = _globalParam?.SpeedParaSysSetting?.SpeedTypeItemCollection?.SpeedTypeItemList?
|
|
.FirstOrDefault(x => x != null && string.Equals(x.AxisName, axisName, StringComparison.OrdinalIgnoreCase));
|
|
|
|
if (speedItem == null)
|
|
{
|
|
throw new InvalidOperationException(string.Format("No speed configuration found for axis {0}.", axisName));
|
|
}
|
|
|
|
return speedItem;
|
|
}
|
|
|
|
private static AxisMotionProfile CreateCustomProfile(SpeedTypeItem speedItem, double speed)
|
|
{
|
|
ValidatePositiveFinite(speed, nameof(speed), speedItem.AxisName);
|
|
ValidatePositiveFinite(speedItem.Acc, nameof(speedItem.Acc), speedItem.AxisName);
|
|
ValidatePositiveFinite(speedItem.Dec, nameof(speedItem.Dec), speedItem.AxisName);
|
|
ValidatePositiveFinite(speedItem.Jerk, nameof(speedItem.Jerk), speedItem.AxisName);
|
|
ValidatePositiveFinite(speedItem.Speed, nameof(speedItem.Speed), speedItem.AxisName);
|
|
|
|
var scale = speed / speedItem.Speed;
|
|
return new AxisMotionProfile(
|
|
speed,
|
|
speedItem.Acc * scale,
|
|
speedItem.Dec * scale,
|
|
speedItem.Jerk * scale);
|
|
}
|
|
|
|
private static AxisMotionProfile CreateProfile(SpeedTypeItem speedItem, AxisSpeedProfile profile)
|
|
{
|
|
if (speedItem == null)
|
|
{
|
|
throw new ArgumentNullException(nameof(speedItem));
|
|
}
|
|
|
|
ValidatePositiveFinite(speedItem.Speed, nameof(speedItem.Speed), speedItem.AxisName);
|
|
ValidatePositiveFinite(speedItem.Acc, nameof(speedItem.Acc), speedItem.AxisName);
|
|
ValidatePositiveFinite(speedItem.Dec, nameof(speedItem.Dec), speedItem.AxisName);
|
|
ValidatePositiveFinite(speedItem.Jerk, nameof(speedItem.Jerk), speedItem.AxisName);
|
|
|
|
var percent = ResolveProfilePercent(speedItem, profile);
|
|
var scale = percent / 100.0d;
|
|
return new AxisMotionProfile(
|
|
speedItem.Speed * scale,
|
|
speedItem.Acc * scale,
|
|
speedItem.Dec * scale,
|
|
speedItem.Jerk * scale);
|
|
}
|
|
|
|
private static double ResolveProfilePercent(SpeedTypeItem speedItem, AxisSpeedProfile profile)
|
|
{
|
|
switch (profile)
|
|
{
|
|
case AxisSpeedProfile.Low:
|
|
return ValidatePercent(speedItem.LowPercent, nameof(speedItem.LowPercent), speedItem.AxisName);
|
|
case AxisSpeedProfile.Medium:
|
|
return ValidatePercent(speedItem.MedianPercent, nameof(speedItem.MedianPercent), speedItem.AxisName);
|
|
case AxisSpeedProfile.High:
|
|
return ValidatePercent(speedItem.HighPercent, nameof(speedItem.HighPercent), speedItem.AxisName);
|
|
default:
|
|
return 100.0d;
|
|
}
|
|
}
|
|
|
|
private static AxisSpeedProfile MapCurrentSpeedTypeText(string currentSpeedTypeText)
|
|
{
|
|
if (string.Equals(currentSpeedTypeText, "High", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
return AxisSpeedProfile.High;
|
|
}
|
|
|
|
if (string.Equals(currentSpeedTypeText, "Median", StringComparison.OrdinalIgnoreCase)
|
|
|| string.Equals(currentSpeedTypeText, "Medium", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
return AxisSpeedProfile.Medium;
|
|
}
|
|
|
|
return AxisSpeedProfile.Low;
|
|
}
|
|
|
|
private static void ValidateMotionProfile(AxisMotionProfile profile, string axisName)
|
|
{
|
|
if (profile == null)
|
|
{
|
|
throw new ArgumentNullException(nameof(profile));
|
|
}
|
|
|
|
ValidatePositiveFinite(profile.Velocity, nameof(profile.Velocity), axisName);
|
|
ValidatePositiveFinite(profile.Acceleration, nameof(profile.Acceleration), axisName);
|
|
ValidatePositiveFinite(profile.Deceleration, nameof(profile.Deceleration), axisName);
|
|
ValidatePositiveFinite(profile.Jerk, nameof(profile.Jerk), axisName);
|
|
}
|
|
|
|
private static void ValidatePositiveFinite(double value, string propertyName, string axisName)
|
|
{
|
|
if (double.IsNaN(value) || double.IsInfinity(value) || value <= 0d)
|
|
{
|
|
throw new ArgumentOutOfRangeException(propertyName, string.Format("Axis {0} has invalid motion parameter {1}: {2}.", axisName, propertyName, value));
|
|
}
|
|
}
|
|
|
|
private static double ValidatePercent(double value, string propertyName, string axisName)
|
|
{
|
|
if (double.IsNaN(value) || double.IsInfinity(value) || value <= 0d || value > 100d)
|
|
{
|
|
throw new ArgumentOutOfRangeException(propertyName, string.Format("Axis {0} has invalid speed profile percent {1}: {2}.", axisName, propertyName, value));
|
|
}
|
|
|
|
return value;
|
|
}
|
|
|
|
private static void LogProfileApplied(string axisName, AxisMotionProfile profile, AxisSpeedProfile profileType, SpeedTypeItem speedItem)
|
|
{
|
|
var source = speedItem != null ? "ConfiguredProfile" : "CustomScaledProfile";
|
|
string.Format(
|
|
"Axis:{0} speed profile applied. Source:{1} Profile:{2} Velocity:{3:F3} Acc:{4:F3} Dec:{5:F3} Jerk:{6:F3}",
|
|
axisName,
|
|
source,
|
|
profileType,
|
|
profile.Velocity,
|
|
profile.Acceleration,
|
|
profile.Deceleration,
|
|
profile.Jerk).LogInfo();
|
|
}
|
|
}
|
|
}
|