将Feature/XP.Common和Feature/XP.Hardware分支合并至Develop/XP.forHardwareAndCommon,完善XPapp注册和相关硬件类库通用类库功能。
This commit is contained in:
@@ -0,0 +1,38 @@
|
||||
using System;
|
||||
using XP.Hardware.MotionControl.Abstractions;
|
||||
using XP.Hardware.MotionControl.Config;
|
||||
using XP.Hardware.Plc.Abstractions;
|
||||
|
||||
namespace XP.Hardware.MotionControl.Implementations
|
||||
{
|
||||
/// <summary>
|
||||
/// 基于 PLC 的轴复位实现 | PLC-based Axis Reset Implementation
|
||||
/// 写入复位信号,读取复位完成信号 | Writes reset signal, reads reset-done signal
|
||||
/// </summary>
|
||||
public class PlcAxisReset : IAxisReset
|
||||
{
|
||||
private readonly ISignalDataService _signalService;
|
||||
private bool _isResetDone;
|
||||
|
||||
public PlcAxisReset(ISignalDataService signalService)
|
||||
{
|
||||
_signalService = signalService ?? throw new ArgumentNullException(nameof(signalService));
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool IsResetDone => _isResetDone;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public MotionResult Reset()
|
||||
{
|
||||
_signalService.EnqueueWrite(MotionSignalNames.Axis_Reset, true);
|
||||
return MotionResult.Ok();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void UpdateStatus()
|
||||
{
|
||||
_isResetDone = _signalService.GetValueByName<bool>(MotionSignalNames.Axis_ResetDone);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
using System;
|
||||
using XP.Hardware.MotionControl.Abstractions;
|
||||
using XP.Hardware.MotionControl.Abstractions.Enums;
|
||||
using XP.Hardware.Plc.Abstractions;
|
||||
|
||||
namespace XP.Hardware.MotionControl.Implementations
|
||||
{
|
||||
/// <summary>
|
||||
/// 基于 PLC 的直线轴实现 | PLC-based Linear Axis Implementation
|
||||
/// 通过 ISignalDataService 与 PLC 通信,信号名称硬编码在 MotionSignalNames 中
|
||||
/// Communicates with PLC via ISignalDataService, signal names hardcoded in MotionSignalNames
|
||||
/// </summary>
|
||||
public class PlcLinearAxis : LinearAxisBase
|
||||
{
|
||||
private readonly ISignalDataService _signalService;
|
||||
private readonly string _readSignal;
|
||||
private readonly string _writeSignal;
|
||||
private readonly string _speedSignal;
|
||||
private readonly string _jogPosSignal;
|
||||
private readonly string _jogNegSignal;
|
||||
private readonly string _homeSignal;
|
||||
private readonly string _stopSignal;
|
||||
private readonly string _limitPosSignal;
|
||||
private readonly string _limitNegSignal;
|
||||
|
||||
/// <summary>
|
||||
/// 构造函数 | Constructor
|
||||
/// </summary>
|
||||
public PlcLinearAxis(
|
||||
AxisId axisId, double min, double max, double origin,
|
||||
ISignalDataService signalService,
|
||||
string readSignal, string writeSignal, string speedSignal,
|
||||
string jogPosSignal, string jogNegSignal,
|
||||
string homeSignal, string stopSignal)
|
||||
: base(axisId, min, max, origin)
|
||||
{
|
||||
_signalService = signalService ?? throw new ArgumentNullException(nameof(signalService));
|
||||
_readSignal = readSignal;
|
||||
_writeSignal = writeSignal;
|
||||
_speedSignal = speedSignal;
|
||||
_jogPosSignal = jogPosSignal;
|
||||
_jogNegSignal = jogNegSignal;
|
||||
_homeSignal = homeSignal;
|
||||
_stopSignal = stopSignal;
|
||||
_limitPosSignal = $"MC_{axisId}_LimitPos";
|
||||
_limitNegSignal = $"MC_{axisId}_LimitNeg";
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override MotionResult MoveToTarget(double target, double? speed = null)
|
||||
{
|
||||
if (!ValidateTarget(target))
|
||||
return MotionResult.Fail($"目标位置 {target} 超出范围 [{_min}, {_max}]");
|
||||
if (Status == AxisStatus.Moving)
|
||||
return MotionResult.Fail($"轴 {_axisId} 正在运动中,拒绝重复命令");
|
||||
|
||||
if (speed.HasValue)
|
||||
_signalService.EnqueueWrite(_speedSignal, (float)speed.Value);
|
||||
_signalService.EnqueueWrite(_writeSignal, (float)target);
|
||||
Status = AxisStatus.Moving;
|
||||
return MotionResult.Ok();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override MotionResult JogStart(bool positive)
|
||||
{
|
||||
if (Status == AxisStatus.Homing)
|
||||
return MotionResult.Fail($"轴 {_axisId} 正在回零,拒绝 Jog 命令");
|
||||
|
||||
var signal = positive ? _jogPosSignal : _jogNegSignal;
|
||||
_signalService.EnqueueWrite(signal, true);
|
||||
return MotionResult.Ok();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override MotionResult JogStop()
|
||||
{
|
||||
_signalService.EnqueueWrite(_jogPosSignal, false);
|
||||
_signalService.EnqueueWrite(_jogNegSignal, false);
|
||||
return MotionResult.Ok();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override MotionResult Home()
|
||||
{
|
||||
_signalService.EnqueueWrite(_homeSignal, true);
|
||||
Status = AxisStatus.Homing;
|
||||
return MotionResult.Ok();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override MotionResult Stop()
|
||||
{
|
||||
_signalService.EnqueueWrite(_stopSignal, true);
|
||||
return MotionResult.Ok();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void UpdateStatus()
|
||||
{
|
||||
ActualPosition = _signalService.GetValueByName<float>(_readSignal);
|
||||
try { PositiveLimitHit = _signalService.GetValueByName<bool>(_limitPosSignal); } catch { }
|
||||
try { NegativeLimitHit = _signalService.GetValueByName<bool>(_limitNegSignal); } catch { }
|
||||
if (PositiveLimitHit || NegativeLimitHit) Status = AxisStatus.Alarm;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using XP.Hardware.MotionControl.Abstractions;
|
||||
using XP.Hardware.MotionControl.Abstractions.Enums;
|
||||
using XP.Hardware.MotionControl.Config;
|
||||
using XP.Hardware.Plc.Abstractions;
|
||||
|
||||
namespace XP.Hardware.MotionControl.Implementations
|
||||
{
|
||||
/// <summary>
|
||||
/// 基于 PLC 的运动系统实现 | PLC-based Motion System Implementation
|
||||
/// 使用 MotionSignalNames 硬编码信号名称 | Uses MotionSignalNames for hardcoded signal names
|
||||
/// </summary>
|
||||
public class PlcMotionSystem : IMotionSystem
|
||||
{
|
||||
private readonly Dictionary<AxisId, ILinearAxis> _linearAxes = new();
|
||||
private readonly Dictionary<RotaryAxisId, IRotaryAxis> _rotaryAxes = new();
|
||||
private readonly ISafetyDoor _safetyDoor;
|
||||
private readonly IAxisReset _axisReset;
|
||||
|
||||
public PlcMotionSystem(MotionControlConfig config, ISignalDataService signalService)
|
||||
{
|
||||
if (config == null) throw new ArgumentNullException(nameof(config));
|
||||
if (signalService == null) throw new ArgumentNullException(nameof(signalService));
|
||||
|
||||
// 创建直线轴 | Create linear axes
|
||||
if (config.LinearAxes.TryGetValue(AxisId.SourceZ, out var sz))
|
||||
_linearAxes[AxisId.SourceZ] = new PlcLinearAxis(AxisId.SourceZ, sz.Min, sz.Max, sz.Origin, signalService,
|
||||
MotionSignalNames.SourceZ_Pos, MotionSignalNames.SourceZ_Target, MotionSignalNames.SourceZ_Speed,
|
||||
MotionSignalNames.SourceZ_JogPos, MotionSignalNames.SourceZ_JogNeg,
|
||||
MotionSignalNames.SourceZ_Home, MotionSignalNames.SourceZ_Stop);
|
||||
|
||||
if (config.LinearAxes.TryGetValue(AxisId.DetectorZ, out var dz))
|
||||
_linearAxes[AxisId.DetectorZ] = new PlcLinearAxis(AxisId.DetectorZ, dz.Min, dz.Max, dz.Origin, signalService,
|
||||
MotionSignalNames.DetZ_Pos, MotionSignalNames.DetZ_Target, MotionSignalNames.DetZ_Speed,
|
||||
MotionSignalNames.DetZ_JogPos, MotionSignalNames.DetZ_JogNeg,
|
||||
MotionSignalNames.DetZ_Home, MotionSignalNames.DetZ_Stop);
|
||||
|
||||
if (config.LinearAxes.TryGetValue(AxisId.StageX, out var sx))
|
||||
_linearAxes[AxisId.StageX] = new PlcLinearAxis(AxisId.StageX, sx.Min, sx.Max, sx.Origin, signalService,
|
||||
MotionSignalNames.StageX_Pos, MotionSignalNames.StageX_Target, MotionSignalNames.StageX_Speed,
|
||||
MotionSignalNames.StageX_JogPos, MotionSignalNames.StageX_JogNeg,
|
||||
MotionSignalNames.StageX_Home, MotionSignalNames.StageX_Stop);
|
||||
|
||||
if (config.LinearAxes.TryGetValue(AxisId.StageY, out var sy))
|
||||
_linearAxes[AxisId.StageY] = new PlcLinearAxis(AxisId.StageY, sy.Min, sy.Max, sy.Origin, signalService,
|
||||
MotionSignalNames.StageY_Pos, MotionSignalNames.StageY_Target, MotionSignalNames.StageY_Speed,
|
||||
MotionSignalNames.StageY_JogPos, MotionSignalNames.StageY_JogNeg,
|
||||
MotionSignalNames.StageY_Home, MotionSignalNames.StageY_Stop);
|
||||
|
||||
// 创建旋转轴 | Create rotary axes
|
||||
if (config.RotaryAxes.TryGetValue(RotaryAxisId.DetectorSwing, out var ds))
|
||||
_rotaryAxes[RotaryAxisId.DetectorSwing] = new PlcRotaryAxis(RotaryAxisId.DetectorSwing, ds.Min, ds.Max, ds.Enabled, signalService,
|
||||
MotionSignalNames.DetSwing_Angle, MotionSignalNames.DetSwing_Target, MotionSignalNames.DetSwing_Speed,
|
||||
MotionSignalNames.DetSwing_JogPos, MotionSignalNames.DetSwing_JogNeg,
|
||||
MotionSignalNames.DetSwing_Home, MotionSignalNames.DetSwing_Stop);
|
||||
|
||||
if (config.RotaryAxes.TryGetValue(RotaryAxisId.StageRotation, out var sr))
|
||||
_rotaryAxes[RotaryAxisId.StageRotation] = new PlcRotaryAxis(RotaryAxisId.StageRotation, sr.Min, sr.Max, sr.Enabled, signalService,
|
||||
MotionSignalNames.StageRot_Angle, MotionSignalNames.StageRot_Target, MotionSignalNames.StageRot_Speed,
|
||||
MotionSignalNames.StageRot_JogPos, MotionSignalNames.StageRot_JogNeg,
|
||||
MotionSignalNames.StageRot_Home, MotionSignalNames.StageRot_Stop);
|
||||
|
||||
if (config.RotaryAxes.TryGetValue(RotaryAxisId.FixtureRotation, out var fr))
|
||||
_rotaryAxes[RotaryAxisId.FixtureRotation] = new PlcRotaryAxis(RotaryAxisId.FixtureRotation, fr.Min, fr.Max, fr.Enabled, signalService,
|
||||
MotionSignalNames.FixRot_Angle, MotionSignalNames.FixRot_Target, MotionSignalNames.FixRot_Speed,
|
||||
MotionSignalNames.FixRot_JogPos, MotionSignalNames.FixRot_JogNeg,
|
||||
MotionSignalNames.FixRot_Home, MotionSignalNames.FixRot_Stop);
|
||||
|
||||
// 创建安全门 | Create safety door
|
||||
_safetyDoor = new PlcSafetyDoor(signalService);
|
||||
|
||||
// 创建轴复位 | Create axis reset
|
||||
_axisReset = new PlcAxisReset(signalService);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ISafetyDoor SafetyDoor => _safetyDoor;
|
||||
/// <inheritdoc/>
|
||||
public IAxisReset AxisReset => _axisReset;
|
||||
/// <inheritdoc/>
|
||||
public IReadOnlyDictionary<AxisId, ILinearAxis> LinearAxes => _linearAxes;
|
||||
/// <inheritdoc/>
|
||||
public IReadOnlyDictionary<RotaryAxisId, IRotaryAxis> RotaryAxes => _rotaryAxes;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ILinearAxis GetLinearAxis(AxisId axisId)
|
||||
{
|
||||
if (_linearAxes.TryGetValue(axisId, out var axis)) return axis;
|
||||
throw new KeyNotFoundException($"未找到直线轴 {axisId} | Linear axis {axisId} not found");
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IRotaryAxis GetRotaryAxis(RotaryAxisId axisId)
|
||||
{
|
||||
if (_rotaryAxes.TryGetValue(axisId, out var axis)) return axis;
|
||||
throw new KeyNotFoundException($"未找到旋转轴 {axisId} | Rotary axis {axisId} not found");
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void UpdateAllStatus()
|
||||
{
|
||||
foreach (var axis in _linearAxes.Values) axis.UpdateStatus();
|
||||
foreach (var axis in _rotaryAxes.Values) axis.UpdateStatus();
|
||||
_safetyDoor.UpdateStatus();
|
||||
_axisReset.UpdateStatus();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
using System;
|
||||
using XP.Hardware.MotionControl.Abstractions;
|
||||
using XP.Hardware.MotionControl.Abstractions.Enums;
|
||||
using XP.Hardware.Plc.Abstractions;
|
||||
|
||||
namespace XP.Hardware.MotionControl.Implementations
|
||||
{
|
||||
/// <summary>
|
||||
/// 基于 PLC 的旋转轴实现 | PLC-based Rotary Axis Implementation
|
||||
/// 信号名称硬编码在 MotionSignalNames 中 | Signal names hardcoded in MotionSignalNames
|
||||
/// </summary>
|
||||
public class PlcRotaryAxis : RotaryAxisBase
|
||||
{
|
||||
private readonly ISignalDataService _signalService;
|
||||
private readonly string _readSignal;
|
||||
private readonly string _writeSignal;
|
||||
private readonly string _speedSignal;
|
||||
private readonly string _jogPosSignal;
|
||||
private readonly string _jogNegSignal;
|
||||
private readonly string _homeSignal;
|
||||
private readonly string _stopSignal;
|
||||
|
||||
/// <summary>
|
||||
/// 构造函数 | Constructor
|
||||
/// </summary>
|
||||
public PlcRotaryAxis(
|
||||
RotaryAxisId axisId, double minAngle, double maxAngle, bool enabled,
|
||||
ISignalDataService signalService,
|
||||
string readSignal, string writeSignal, string speedSignal,
|
||||
string jogPosSignal, string jogNegSignal,
|
||||
string homeSignal, string stopSignal)
|
||||
: base(axisId, minAngle, maxAngle, enabled)
|
||||
{
|
||||
_signalService = signalService ?? throw new ArgumentNullException(nameof(signalService));
|
||||
_readSignal = readSignal;
|
||||
_writeSignal = writeSignal;
|
||||
_speedSignal = speedSignal;
|
||||
_jogPosSignal = jogPosSignal;
|
||||
_jogNegSignal = jogNegSignal;
|
||||
_homeSignal = homeSignal;
|
||||
_stopSignal = stopSignal;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override MotionResult MoveToTarget(double targetAngle, double? speed = null)
|
||||
{
|
||||
if (!_enabled) return MotionResult.Fail($"旋转轴 {_axisId} 已禁用,拒绝移动命令");
|
||||
if (!ValidateTarget(targetAngle)) return MotionResult.Fail($"目标角度 {targetAngle} 超出范围 [{_minAngle}, {_maxAngle}]");
|
||||
if (Status == AxisStatus.Moving) return MotionResult.Fail($"旋转轴 {_axisId} 正在运动中,拒绝重复命令");
|
||||
|
||||
if (speed.HasValue)
|
||||
_signalService.EnqueueWrite(_speedSignal, (float)speed.Value);
|
||||
_signalService.EnqueueWrite(_writeSignal, (float)targetAngle);
|
||||
Status = AxisStatus.Moving;
|
||||
return MotionResult.Ok();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override MotionResult JogStart(bool positive)
|
||||
{
|
||||
if (!_enabled) return MotionResult.Fail($"旋转轴 {_axisId} 已禁用,拒绝 Jog 命令");
|
||||
if (Status == AxisStatus.Homing) return MotionResult.Fail($"旋转轴 {_axisId} 正在回零,拒绝 Jog 命令");
|
||||
|
||||
_signalService.EnqueueWrite(positive ? _jogPosSignal : _jogNegSignal, true);
|
||||
return MotionResult.Ok();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override MotionResult JogStop()
|
||||
{
|
||||
if (!_enabled) return MotionResult.Fail($"旋转轴 {_axisId} 已禁用,拒绝 Jog 停止命令");
|
||||
_signalService.EnqueueWrite(_jogPosSignal, false);
|
||||
_signalService.EnqueueWrite(_jogNegSignal, false);
|
||||
return MotionResult.Ok();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override MotionResult Home()
|
||||
{
|
||||
if (!_enabled) return MotionResult.Fail($"旋转轴 {_axisId} 已禁用,拒绝回零命令");
|
||||
_signalService.EnqueueWrite(_homeSignal, true);
|
||||
Status = AxisStatus.Homing;
|
||||
return MotionResult.Ok();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override MotionResult Stop()
|
||||
{
|
||||
if (!_enabled) return MotionResult.Fail($"旋转轴 {_axisId} 已禁用,拒绝停止命令");
|
||||
_signalService.EnqueueWrite(_stopSignal, true);
|
||||
return MotionResult.Ok();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void UpdateStatus()
|
||||
{
|
||||
if (!_enabled) return;
|
||||
ActualAngle = _signalService.GetValueByName<float>(_readSignal);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
using System;
|
||||
using XP.Hardware.MotionControl.Abstractions;
|
||||
using XP.Hardware.MotionControl.Abstractions.Enums;
|
||||
using XP.Hardware.MotionControl.Config;
|
||||
using XP.Hardware.Plc.Abstractions;
|
||||
|
||||
namespace XP.Hardware.MotionControl.Implementations
|
||||
{
|
||||
/// <summary>
|
||||
/// 基于 PLC 的安全门实现 | PLC-based Safety Door Implementation
|
||||
/// 信号名称硬编码在 MotionSignalNames 中 | Signal names hardcoded in MotionSignalNames
|
||||
/// </summary>
|
||||
public class PlcSafetyDoor : SafetyDoorBase
|
||||
{
|
||||
private readonly ISignalDataService _signalService;
|
||||
private bool _isInterlocked;
|
||||
|
||||
public PlcSafetyDoor(ISignalDataService signalService)
|
||||
{
|
||||
_signalService = signalService ?? throw new ArgumentNullException(nameof(signalService));
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool IsInterlocked => _isInterlocked;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override MotionResult Open()
|
||||
{
|
||||
if (IsInterlocked)
|
||||
return MotionResult.Fail("联锁信号有效,禁止开门 | Interlock active, door open blocked");
|
||||
_signalService.EnqueueWrite(MotionSignalNames.Door_Open, true);
|
||||
_status = DoorStatus.Opening;
|
||||
return MotionResult.Ok();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override MotionResult Close()
|
||||
{
|
||||
_signalService.EnqueueWrite(MotionSignalNames.Door_Close, true);
|
||||
_status = DoorStatus.Closing;
|
||||
return MotionResult.Ok();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override MotionResult Stop()
|
||||
{
|
||||
_signalService.EnqueueWrite(MotionSignalNames.Door_Stop, true);
|
||||
return MotionResult.Ok();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void UpdateStatus()
|
||||
{
|
||||
_isInterlocked = _signalService.GetValueByName<byte>(MotionSignalNames.Door_Interlock) == 10;
|
||||
var statusValue = _signalService.GetValueByName<int>(MotionSignalNames.Door_Status);
|
||||
_status = statusValue switch
|
||||
{
|
||||
1 => DoorStatus.Opening,
|
||||
2 => DoorStatus.Open,
|
||||
3 => DoorStatus.Closing,
|
||||
4 => DoorStatus.Closed,
|
||||
5 => DoorStatus.Locked,
|
||||
6 => DoorStatus.Error,
|
||||
_ => DoorStatus.Unknown
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user