将Feature/XP.Common和Feature/XP.Hardware分支合并至Develop/XP.forHardwareAndCommon,完善XPapp注册和相关硬件类库通用类库功能。

This commit is contained in:
QI Mingxuan
2026-04-16 17:31:13 +08:00
parent 6ec4c3ddaa
commit 2bd6e566c3
581 changed files with 74600 additions and 222 deletions
@@ -0,0 +1,494 @@
# XP.Hardware.MotionControl 运动控制模块设计规划
> 文档版本:v1.0
> 创建日期:2026-04-10
> 适用项目:XplorePlane 平面CT工业检测系统
---
## 1. 背景与硬件描述
本模块用于控制平面CT系统中的所有运动轴、旋转轴及安全防护门。
### 1.1 直线轴(4轴)
| 轴标识 | 说明 | 运动方向 |
|--------|------|----------|
| 轴1 SourceZ | 射线源Z方向运动 | 上下 |
| 轴2 DetectorZ | 探测器Z方向运动 | 上下 |
| 轴3 StageX | 载物台X方向运动 | 前后 |
| 轴4 StageY | 载物台Y方向运动 | 左右 |
### 1.2 旋转轴(3轴)
| 轴标识 | 说明 | 特点 |
|--------|------|------|
| 旋转轴1 DetectorSwing | 探测器在XZ平面的旋转摆动 | 有限角度,旋转中心在射线源Z方向延长线上 |
| 旋转轴2 StageRotation | 载物台围绕XY中心的Z方向旋转 | 可选配置 |
| 旋转轴3 FixtureRotation | 旋转夹具在载物台XY平面上围绕X方向的旋转 | 可选配置 |
### 1.3 安全门
防护门,具备状态机控制与互锁信号联动。
### 1.4 几何关系
```
FOD(焦点到旋转中心距离)= |SourceZ绝对坐标 - 旋转中心绝对Z坐标|
FDD(焦点到探测器距离) = |SourceZ绝对坐标 - DetectorZ绝对坐标|
放大倍率 M = FDD / FOD
```
只有确定了射线源、探测器、载物台旋转中心的绝对坐标,才能准确计算 FOD 和 FDD,进而控制放大倍率。
---
## 2. 目录结构
```
XP.Hardware.MotionControl/
├── Abstractions/
│ ├── ILinearAxis.cs # 直线轴策略接口
│ ├── IRotaryAxis.cs # 旋转轴策略接口
│ ├── ISafetyDoor.cs # 安全门策略接口
│ ├── IMotionSystem.cs # 顶层系统管理接口
│ ├── LinearAxisBase.cs # 直线轴抽象基类
│ ├── RotaryAxisBase.cs # 旋转轴抽象基类
│ ├── SafetyDoorBase.cs # 安全门抽象基类
│ ├── MotionResult.cs # 统一操作结果类型
│ └── Enums/
│ ├── AxisStatus.cs # 轴状态枚举
│ ├── DoorStatus.cs # 门状态枚举
│ └── AxisId.cs # 轴标识枚举
├── Implementations/
│ ├── PlcLinearAxis.cs # 基于PLC的直线轴实现
│ ├── PlcRotaryAxis.cs # 基于PLC的旋转轴实现
│ └── PlcSafetyDoor.cs # 基于PLC的安全门实现
├── Services/
│ ├── IMotionControlService.cs # 运动控制业务服务接口
│ ├── MotionControlService.cs # 服务实现(含轮询线程、几何计算)
│ └── GeometryCalculator.cs # FOD/FDD/放大倍率计算器
├── Config/
│ ├── MotionControlConfig.cs # 配置实体(轴范围、原点、PLC信号名)
│ └── ConfigLoader.cs # 配置加载器
├── ViewModels/
│ └── MotionControlViewModel.cs # 操作面板 ViewModel
├── Views/
│ └── MotionControlView.xaml # 操作面板 UserControl(宽350
├── Module/
│ └── MotionControlModule.cs # Prism 模块注册
├── Documents/
│ └── MotionControl_Design.md # 本文档
└── Resources/
├── Resources.resx
├── Resources.zh-CN.resx
└── Resources.en-US.resx
```
---
## 3. 核心抽象层设计
### 3.1 三类硬件抽象继承关系
```
ILinearAxis IRotaryAxis ISafetyDoor
│ │ │
LinearAxisBase RotaryAxisBase SafetyDoorBase
│ │ │
PlcLinearAxis PlcRotaryAxis PlcSafetyDoor
```
### 3.2 ILinearAxis 接口职责
- 位置读取(实际值)
- 目标位置写入
- Jog 正向 / Jog 反向(按住运动,松开停止)
- 回零
- 停止
- 限位状态读取(正限位 / 负限位)
- 轴状态(Idle / Moving / Homing / Error / Alarm
### 3.3 IRotaryAxis 接口职责
- 角度读取(实际值)
- 目标角度写入
- Jog 正向 / Jog 反向
- 回零
- 停止
- 角度软限位(MinAngle / MaxAngle,来自配置)
- 轴状态
### 3.4 ISafetyDoor 接口职责
- 状态机读取(Unknown / Opening / Open / Closing / Closed / Locked / Error
- 开门指令
- 关门指令
- 互锁信号读取(是否允许开门)
### 3.5 IMotionSystem 顶层接口职责
-`AxisId` 枚举索引各直线轴(SourceZ、DetectorZ、StageX、StageY
-`RotaryAxisId` 枚举索引各旋转轴(DetectorSwing、StageRotation、FixtureRotation
- 持有安全门实例
- 协调运动:开门前联锁检查
- 几何计算委托给 `GeometryCalculator`
---
## 4. 服务层设计
### 4.1 IMotionControlService
封装业务规则,供 ViewModel 调用:
- `StartPolling()` / `StopPolling()` — 启停 PLC 数据轮询
- `MoveToTarget(AxisId, double)` — 移动指定轴到目标位置
- `MoveAllToTarget(targets)` — 多轴联动移动
- `StopAll()` — 停止所有轴
- `HomeAll()` — 全部回零
- `JogStart(AxisId, direction)` / `JogStop(AxisId)` — Jog 控制
- `OpenDoor()` / `CloseDoor()` / `StopDoor()` — 门控制(含联锁前置检查)
- `ApplyGeometry(fod, fdd)` — 根据 FOD/FDD 反算并移动
- `GetCurrentGeometry()` — 获取当前 FOD/FDD/M
### 4.2 GeometryCalculator
```csharp
// 正算:由轴位置计算几何参数
double CalcFOD(double sourceZAbs, double stageRotCenterZAbs)
double CalcFDD(double sourceZAbs, double detectorZAbs)
double CalcMagnification(double fdd, double fod)
// 反算:由目标几何参数计算轴目标位置
(double sourceZTarget, double detectorZTarget) CalcAxisTargets(double targetFOD, double targetFDD)
// 边界检查:目标值是否在轴 Min/Max 范围内
bool ValidateTargets(double sourceZTarget, double detectorZTarget)
```
绝对坐标计算方式:
```
SourceZ绝对坐标 = SourceZ当前位置 + SourceZOrigin(配置)
DetectorZ绝对坐标 = DetectorZ当前位置 + DetectorZOrigin(配置)
旋转中心绝对Z坐标 = StageRotationCenterZ(配置,固定值)
```
### 4.3 轮询线程
`MotionControlService` 内部使用 `System.Threading.Timer`,周期约 100ms,仅在 PLC 已连接时激活。每次轮询通过 `ISignalDataService.GetValueByName()` 读取所有轴的实际位置和状态,更新到 ViewModel 的可绑定属性。
---
## 5. 与 PLC 模块的依赖关系
MotionControl 通过依赖注入获取 `ISignalDataService`(来自 XP.Hardware.PLC),所有读写操作通过信号逻辑名称进行,不直接操作 PLC 地址。信号名称在配置文件中定义。
```
XP.Hardware.MotionControl
└── 依赖注入 → ISignalDataServiceXP.Hardware.PLC
└── 读:GetValueByName<float>("MC_SourceZ_Pos")
└── 写:EnqueueWrite("MC_SourceZ_Target", value)
```
---
## 6. 配置文件设计(App.config
```xml
<MotionControl>
<!-- 直线轴配置(单位:mm -->
<SourceZ Min="0" Max="500" Origin="0"
PlcSignalRead="MC_SourceZ_Pos" PlcSignalWrite="MC_SourceZ_Target"
PlcSignalJogPos="MC_SourceZ_JogPos" PlcSignalJogNeg="MC_SourceZ_JogNeg"
PlcSignalHome="MC_SourceZ_Home" PlcSignalStop="MC_SourceZ_Stop" />
<DetectorZ Min="0" Max="600" Origin="0"
PlcSignalRead="MC_DetZ_Pos" PlcSignalWrite="MC_DetZ_Target"
PlcSignalJogPos="MC_DetZ_JogPos" PlcSignalJogNeg="MC_DetZ_JogNeg"
PlcSignalHome="MC_DetZ_Home" PlcSignalStop="MC_DetZ_Stop" />
<StageX Min="-150" Max="150" Origin="0"
PlcSignalRead="MC_StageX_Pos" PlcSignalWrite="MC_StageX_Target"
PlcSignalJogPos="MC_StageX_JogPos" PlcSignalJogNeg="MC_StageX_JogNeg"
PlcSignalHome="MC_StageX_Home" PlcSignalStop="MC_StageX_Stop" />
<StageY Min="-150" Max="150" Origin="0"
PlcSignalRead="MC_StageY_Pos" PlcSignalWrite="MC_StageY_Target"
PlcSignalJogPos="MC_StageY_JogPos" PlcSignalJogNeg="MC_StageY_JogNeg"
PlcSignalHome="MC_StageY_Home" PlcSignalStop="MC_StageY_Stop" />
<!-- 旋转轴配置(单位:度) -->
<DetectorSwing Min="-45" Max="45" Origin="0" Enabled="true"
PlcSignalRead="MC_DetSwing_Angle" PlcSignalWrite="MC_DetSwing_Target"
PlcSignalJogPos="MC_DetSwing_JogPos" PlcSignalJogNeg="MC_DetSwing_JogNeg"
PlcSignalHome="MC_DetSwing_Home" PlcSignalStop="MC_DetSwing_Stop" />
<StageRotation Min="-360" Max="360" Origin="0" Enabled="true"
PlcSignalRead="MC_StageRot_Angle" PlcSignalWrite="MC_StageRot_Target"
PlcSignalJogPos="MC_StageRot_JogPos" PlcSignalJogNeg="MC_StageRot_JogNeg"
PlcSignalHome="MC_StageRot_Home" PlcSignalStop="MC_StageRot_Stop" />
<FixtureRotation Min="-90" Max="90" Origin="0" Enabled="false"
PlcSignalRead="MC_FixRot_Angle" PlcSignalWrite="MC_FixRot_Target"
PlcSignalJogPos="MC_FixRot_JogPos" PlcSignalJogNeg="MC_FixRot_JogNeg"
PlcSignalHome="MC_FixRot_Home" PlcSignalStop="MC_FixRot_Stop" />
<!-- 安全门配置 -->
<SafetyDoor PlcSignalOpen="MC_Door_Open"
PlcSignalClose="MC_Door_Close"
PlcSignalStop="MC_Door_Stop"
PlcSignalStatus="MC_Door_Status"
PlcSignalInterlock="MC_Door_Interlock" />
<!-- 几何原点(绝对坐标 mm,用于 FOD/FDD 计算) -->
<Geometry SourceZOrigin="0"
DetectorZOrigin="600"
StageRotationCenterZ="300" />
<!-- 轮询周期(ms -->
<PollingInterval>100</PollingInterval>
<!-- 运动速度(%0-100 -->
<DefaultVelocity>30</DefaultVelocity>
<JogVelocity>10</JogVelocity>
</MotionControl>
```
---
## 7. ViewModel 设计
### 7.1 属性分组
```csharp
// 直线轴(每轴一组实际值 + 目标值)
double SourceZActual / SourceZTarget
double DetectorZActual / DetectorZTarget
double StageXActual / StageXTarget
double StageYActual / StageYTarget
// 旋转轴(每轴一组,可选轴按配置控制 Visibility)
double DetSwingActual / DetSwingTarget // 附带角度限制提示文本
double StageRotActual / StageRotTarget
double FixtureRotActual / FixtureRotTarget
// 几何信息(实时计算,只读显示)
double FOD / FDD / Magnification
// 几何反算输入
double TargetFOD / TargetFDD / TargetMagnification
// 安全门
DoorStatus DoorStatus
bool IsInterlocked
// 状态
bool IsMoving
bool IsPlcConnected
// 调试模式(默认 false,折叠隐藏调试区域)
bool IsDebugMode
```
### 7.2 命令列表
```csharp
// 主操作命令
ICommand MoveCommand // 执行运动到各轴目标位置
ICommand StopCommand // 停止所有轴
ICommand HomeCommand // 全部回零
ICommand OpenDoorCommand
ICommand CloseDoorCommand
ICommand StopDoorCommand
ICommand ApplyGeometryCommand // 根据 FOD/FDD/M 反算并移动
// 调试命令(Jog,按住触发,松开停止)
ICommand JogPlusCommand // 参数:AxisId
ICommand JogMinusCommand // 参数:AxisId
ICommand JogStopCommand // 参数:AxisId
```
---
## 8. View 布局设计(宽 350pxUserControl
```
┌─────────────────────────────────────┐ 350px
│ [安全门状态] ● 已关闭 [互锁: 正常] │
├─────────────────────────────────────┤
│ 直线轴 │
│ 源Z [实际: 123.4mm] [▲目标▼] │
│ 探Z [实际: 456.7mm] [▲目标▼] │
│ 台X [实际: 12.3mm] [▲目标▼] │
│ 台Y [实际: -5.6mm] [▲目标▼] │
├─────────────────────────────────────┤
│ 旋转轴 │
│ 探摆 [实际: 15.0°] [▲目标▼] │
│ 限制: -45° ~ +45° │
│ 台转 [实际: 180.0°] [▲目标▼] │
│ 夹具 [实际: 0.0°] [▲目标▼] │
├─────────────────────────────────────┤
│ 几何信息 │
│ FOD: 150.0mm FDD: 450.0mm M: 3.0│
│ [目标FOD▲▼] [目标FDD▲▼] [应用] │
├─────────────────────────────────────┤
│ [开始运动] [停止运动] [回零] │
│ [开门] [关门] │
├─────────────────────────────────────┤
│ ▼ 调试模式(默认折叠) │
│ 源Z [+Jog][-Jog] 探Z [+][-] │
│ 台X [+Jog][-Jog] 台Y [+][-] │
│ 探摆 [+Jog][-Jog] 台转[+][-] │
│ 夹具 [+Jog][-Jog] │
└─────────────────────────────────────┘
```
---
## 9. FOD/FDD 反算逻辑
用户输入目标 FOD 和 FDD(或放大倍率 M),系统自动计算各轴目标位置:
```
// 已知:StageRotationCenterZ(旋转中心绝对Z坐标,来自配置,固定值)
// 输入 FOD + FDD
SourceZ_target = StageRotationCenterZ - FOD
DetectorZ_target = SourceZ_target + FDD
// 输入 FOD + MM = FDD/FOD):
FDD = M * FOD
DetectorZ_target = SourceZ_target + FDD
// 边界检查:
if SourceZ_target < SourceZ.Min || SourceZ_target > SourceZ.Max → 拒绝并提示
if DetectorZ_target < DetectorZ.Min || DetectorZ_target > DetectorZ.Max → 拒绝并提示
```
---
## 10. 关键安全机制
参考旧项目 `ControlDoor.openDoor()``_130kvMove` 的实现经验:
| 机制 | 说明 | 对标旧代码 |
|------|------|-----------|
| 运动中防重入 | `IsMoving` 标志位,运动中禁止再次触发运动命令 | `fdcRun` 标志位 |
| 开门联锁检查 | 开门前通过 `IEventAggregator` 检查是否有扫描进行中 | `ControlDoor.openDoor()` 前置逻辑 |
| 射线源联锁 | 开门前需确认射线源已关闭(暂不实现,预留扩展点,见下方说明) | `zeroXray2Correct()` 等待归零 |
| 可选轴隐藏 | `StageRotation``FixtureRotation` 通过配置 `Enabled` 控制 UI 可见性 | 无对应,新增 |
| Jog 安全 | Jog 操作通过 `MouseDown/Up` 事件控制,松开立即停止 | `radButton3_MouseDown/Up` |
---
## 11. Prism 模块注册
```csharp
// MotionControlModule.RegisterTypes()
containerRegistry.RegisterSingleton<MotionControlConfig>(); // 配置(单例)
containerRegistry.RegisterSingleton<IMotionSystem, PlcMotionSystem>(); // 系统管理(单例)
containerRegistry.RegisterSingleton<IMotionControlService, MotionControlService>(); // 服务(单例)
// MotionControlModule.OnInitialized()
// 注册多语言资源到 Fallback Chain
// 启动 PLC 轮询(若 PLC 已连接)
```
---
---
## 12. 关于射线源联锁的扩展说明(暂不实现)
### 12.1 什么是事件聚合器(EventAggregator
Prism 的 `IEventAggregator` 是一个**发布/订阅总线**,用于在模块之间传递消息,而不需要两个模块直接互相引用。
类比理解:就像一个广播电台,`RaySource` 模块是"电台"(发布者),`MotionControl` 模块是"收音机"(订阅者),两者只通过"频道"(事件类型)通信,彼此不知道对方的存在。
```
XP.Hardware.RaySource XP.Hardware.MotionControl
│ │
│ 发布 RaySourceStatusChangedEvent │
└──────────→ EventAggregator ──────→ 订阅并响应
```
### 12.2 如何订阅(当需要实现时)
**第一步:RaySource 模块已有的事件定义**
```csharp
// XP.Hardware.RaySource/Abstractions/Events/RaySourceStatusChangedEvent.cs
// 该事件已存在,携带射线源当前状态
public class RaySourceStatusChangedEvent : PubSubEvent<RaySourceStatus> { }
```
**第二步:MotionControlService 中注入并订阅**
```csharp
public class MotionControlService : IMotionControlService
{
private readonly IEventAggregator _eventAggregator;
private bool _isRaySourceOn = false; // 缓存射线源状态
public MotionControlService(IEventAggregator eventAggregator, ...)
{
_eventAggregator = eventAggregator;
// 订阅射线源状态变化事件
_eventAggregator
.GetEvent<RaySourceStatusChangedEvent>()
.Subscribe(OnRaySourceStatusChanged, ThreadOption.BackgroundThread);
}
private void OnRaySourceStatusChanged(RaySourceStatus status)
{
// 根据状态判断射线是否开启
_isRaySourceOn = (status == RaySourceStatus.XRayOn);
}
public async Task OpenDoor()
{
// 开门前检查射线源
if (_isRaySourceOn)
{
_logger.Warn("射线源未关闭,禁止开门 | Ray source is on, door open blocked");
return;
}
// ... 继续开门逻辑
}
}
```
**第三步:注意事项**
- 订阅时选择 `ThreadOption.BackgroundThread`,避免在 UI 线程处理硬件状态
- 模块卸载时需要取消订阅,防止内存泄漏:`GetEvent<...>().Unsubscribe(OnRaySourceStatusChanged)`
- 两个模块之间**不需要互相引用**,只需要共同引用定义事件的程序集(或在各自模块内定义相同结构的事件)
### 12.3 预留扩展点
当需要实现射线源联锁时,只需在 `MotionControlService.OpenDoor()` 方法开头加入上述订阅逻辑即可,不需要修改任何其他代码。
---
## 13. 事件定义(Prism EventAggregator
```
Abstractions/Events/
├── AxisStatusChangedEvent.cs # 轴状态变化事件(AxisId + AxisStatus
├── DoorStatusChangedEvent.cs # 门状态变化事件(DoorStatus
├── GeometryUpdatedEvent.cs # 几何参数更新事件(FOD + FDD + M
└── MotionErrorEvent.cs # 运动错误事件(AxisId + 错误信息)
```
---
## 13. 实现优先级建议
| 优先级 | 内容 |
|--------|------|
| P0 | 配置实体、枚举定义、MotionResult |
| P0 | ILinearAxis / IRotaryAxis / ISafetyDoor 接口 + 抽象基类 |
| P1 | PlcLinearAxis / PlcRotaryAxis / PlcSafetyDoor 实现 |
| P1 | GeometryCalculator |
| P1 | IMotionControlService + MotionControlService(含轮询) |
| P2 | MotionControlViewModel |
| P2 | MotionControlView.xaml(宽350 UserControl |
| P3 | MotionControlModule 注册 |
| P3 | 多语言资源文件 |