From 0587f7ac6c38b5b3d66eeef171625a8ba9beb5a0 Mon Sep 17 00:00:00 2001 From: chen Date: Fri, 3 Jul 2026 10:01:06 +0000 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=AE=9E=E7=8E=B0=20Axis=20=E6=A0=B8?= =?UTF-8?q?=E5=BF=83=E5=B1=9E=E6=80=A7=E4=B8=8E=20MC=5FPower=20=E5=8A=9F?= =?UTF-8?q?=E8=83=BD=E5=9D=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Axis: 新增运动学/功率/错误管理属性(_position 等 10+ 项) - Axis: 新增 doPower()/readState()/cycle() 接口骨架 - MC_Power: 实现功率使能功能块(边沿检测 + 状态输出) - GlobalResource: 新增 CreateAxis/GetAxisRefById 全局轴管理 - App/CMakeLists.txt: 添加 GlobalResource.cpp 源文件 - docs: 更新 Axis 属性清单与 MC_Power 设计文档 --- App/CMakeLists.txt | 2 + App/Inc/GlobalResource.h | 35 +++++++ App/Inc/UserAppThread.h | 20 ++++ App/Src/GlobalResource.cpp | 60 +++++++++++ App/Src/UserAppThread.cpp | 30 ++++++ App/Src/main.cpp | 14 +-- MotionLib/Inc/Axis.h | 30 +++++- MotionLib/Inc/FunctionBlocks.h | 13 +++ MotionLib/Inc/MC_Power.h | 44 ++++++++ MotionLib/Src/Axis.cpp | 77 +++++++++++++- MotionLib/Src/MC_Power.cpp | 84 +++++++++++++++ docs/design/Axis-Attributes.md | 185 ++++++++++++++++++++------------- docs/design/MC_Power.md | 185 +++++++++++++++++++++++++++++++++ 13 files changed, 685 insertions(+), 94 deletions(-) create mode 100644 App/Inc/GlobalResource.h create mode 100644 App/Inc/UserAppThread.h create mode 100644 App/Src/GlobalResource.cpp create mode 100644 App/Src/UserAppThread.cpp create mode 100644 MotionLib/Inc/FunctionBlocks.h create mode 100644 MotionLib/Inc/MC_Power.h create mode 100644 MotionLib/Src/MC_Power.cpp create mode 100644 docs/design/MC_Power.md diff --git a/App/CMakeLists.txt b/App/CMakeLists.txt index c2ac154..64f1448 100644 --- a/App/CMakeLists.txt +++ b/App/CMakeLists.txt @@ -9,6 +9,8 @@ project(MotionApp set(SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/Src/main.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Src/MotionTickThread.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/Src/UserAppThread.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/Src/GlobalResource.cpp ) # 工程源文件 diff --git a/App/Inc/GlobalResource.h b/App/Inc/GlobalResource.h new file mode 100644 index 0000000..7ea3565 --- /dev/null +++ b/App/Inc/GlobalResource.h @@ -0,0 +1,35 @@ +/** + * @file GlobalResource.h + * @author + * @brief + * @version 0.1 + * @date 2026-07-03 + * + * @copyright Copyright (c) 2026 + * + */ + +#pragma once + +#include "Axis.h" +#include "Types.h" + +/** + * \brief 创建轴并注册到全局表 + * \param id 轴编号 + * \param name 轴名称 + * \return 轴引用。若 id 已存在,返回已有轴的引用(不覆盖) + */ +plcopen::AXIS_REF& CreateAxis(plcopen::UINT id, const std::string& name); + +/** + * \brief 通过编号获取轴引用 + * \param id 轴编号 + * \return AXIS_REF 引用。若不存在,返回 axis==nullptr 的哨兵引用 + */ +plcopen::AXIS_REF& GetAxisRefById(plcopen::UINT id); + + + + + diff --git a/App/Inc/UserAppThread.h b/App/Inc/UserAppThread.h new file mode 100644 index 0000000..0e31d97 --- /dev/null +++ b/App/Inc/UserAppThread.h @@ -0,0 +1,20 @@ +/** + * @file UserAppThread.h + * @author + * @brief + * @version 0.1 + * @date 2026-07-03 + * + * @copyright Copyright (c) 2026 + * + */ + +#pragma once + +int UserAppThread(); + + + + + + diff --git a/App/Src/GlobalResource.cpp b/App/Src/GlobalResource.cpp new file mode 100644 index 0000000..bd79301 --- /dev/null +++ b/App/Src/GlobalResource.cpp @@ -0,0 +1,60 @@ +/** + * @file GlobalResource.cpp + * @author + * @brief 全局轴资源管理 — 持有并管理所有轴实例 + * @version 0.1 + * @date 2026-07-03 + * + * @copyright Copyright (c) 2026 + * + */ + +#include "GlobalResource.h" +#include +#include +#include + +namespace { + +// 轴实例存储 — 拥有所有轴的内存 +std::vector g_axes; + +// 快速查找表:id → AXIS_REF& (指向 g_axes 中的元素) +std::unordered_map g_indexMap; + +// 线程安全 +std::mutex g_mutex; + +// id 不存在时返回的哨兵 +plcopen::AXIS_REF g_invalidRef{0, "", nullptr}; + +} // namespace + +plcopen::AXIS_REF& CreateAxis(plcopen::UINT id, const std::string& name) { + std::lock_guard lock(g_mutex); + + // id 已存在 → 返回已有轴的引用 + auto it = g_indexMap.find(id); + if (it != g_indexMap.end()) { + return g_axes[it->second].ref(); + } + + // 新建轴 + g_axes.emplace_back(id, name); + std::size_t idx = g_axes.size() - 1; + g_indexMap[id] = idx; + return g_axes[idx].ref(); +} + +plcopen::AXIS_REF& GetAxisRefById(plcopen::UINT id) { + std::lock_guard lock(g_mutex); + + auto it = g_indexMap.find(id); + if (it != g_indexMap.end()) { + return g_axes[it->second].ref(); + } + + g_invalidRef.axisNo = id; + g_invalidRef.axis = nullptr; + return g_invalidRef; +} diff --git a/App/Src/UserAppThread.cpp b/App/Src/UserAppThread.cpp new file mode 100644 index 0000000..fcf5da3 --- /dev/null +++ b/App/Src/UserAppThread.cpp @@ -0,0 +1,30 @@ +/** + * @file UserAppThread.cpp + * @author + * @brief + * @version 0.1 + * @date 2026-07-03 + * + * @copyright Copyright (c) 2026 + * + */ + +#include "UserAppThread.h" +#include "Axis.h" +#include "FunctionBlocks.h" +#include "MC_Power.h" + +int UserAppThread() +{ + plcopen::MC_Power mcPowerInstance0() + + while(1) + { + + } + + return 0; +} + + + diff --git a/App/Src/main.cpp b/App/Src/main.cpp index c63f907..1308bbd 100644 --- a/App/Src/main.cpp +++ b/App/Src/main.cpp @@ -13,17 +13,14 @@ #include #include #include "MotionTickThread.h" +#include "UserAppThread.h" #include "Axis.h" #include "Types.h" -static int UserAppThread(); - - std::vector axisList; int main() { - plcopen::Axis axis1(1, "axis1"); plcopen::Axis axis2(2, "axis2"); axisList.push_back(axis1); @@ -39,14 +36,5 @@ int main() } -static int UserAppThread() -{ - while(1) - { - - } - - return 0; -} diff --git a/MotionLib/Inc/Axis.h b/MotionLib/Inc/Axis.h index f508b73..512e184 100644 --- a/MotionLib/Inc/Axis.h +++ b/MotionLib/Inc/Axis.h @@ -59,12 +59,38 @@ public: explicit Axis(UINT axisNo = 1, std::string name = "Axis1"); void cycle(); + MC_AXIS_STATE readState(); + void doPower(bool en); + + /** \brief 获取轴编号 */ + UINT axisNo() const { return _ref.axisNo; } + + /** \brief 获取内部 AXIS_REF(供全局注册表使用) */ + AXIS_REF& ref() { return _ref; } private: - AXIS_REF ref_; - MC_AXIS_STATE state_ = MC_AXIS_STATE::Disabled; + AXIS_REF _ref; + MC_AXIS_STATE _state = MC_AXIS_STATE::Disabled; + MC_AXIS_STATE _resultingSstate = MC_AXIS_STATE::Disabled; + + MotionCommand _activeCommand; + // 运动学属性 + REAL _position; + REAL _actualPosition; + REAL _velocity; + REAL _actualVelocity; + REAL _acceleration; + REAL _actualAcceleration; + + // 功率与使能 + BOOL _powerEnabled; + BOOL _homed; + + // 错误管理 + MC_ERROR_ID _errorId; + BOOL _errorStop; }; } \ No newline at end of file diff --git a/MotionLib/Inc/FunctionBlocks.h b/MotionLib/Inc/FunctionBlocks.h new file mode 100644 index 0000000..61a258a --- /dev/null +++ b/MotionLib/Inc/FunctionBlocks.h @@ -0,0 +1,13 @@ +/** + * @file FunctionBlocks.h + * @author + * @brief + * @version 0.1 + * @date 2026-07-03 + * + * @copyright Copyright (c) 2026 + * + */ + + +#include "MC_Power.h" diff --git a/MotionLib/Inc/MC_Power.h b/MotionLib/Inc/MC_Power.h new file mode 100644 index 0000000..09554b6 --- /dev/null +++ b/MotionLib/Inc/MC_Power.h @@ -0,0 +1,44 @@ +/** + * @file MC_Power.h + * @author + * @brief + * @version 0.1 + * @date 2026-07-03 + * + * @copyright Copyright (c) 2026 + * + */ + +#pragma once + +#include "Axis.h" +#include "Types.h" + +namespace plcopen { +class MC_Power{ +public: + + explicit MC_Power(AXIS_REF& axisRef); + + void cycle(); + + // --- 输入参数 (VAR_INPUT) --- + BOOL Enable = false; + BOOL Enable_Positive = true; + BOOL Enable_Negative = true; + + // --- 输出参数 (VAR_OUTPUT) --- + BOOL Status = false; + BOOL Busy = false; + BOOL Error = false; + MC_ERROR_ID ErrorID = MC_ERROR_ID::mcNoError; + +private: + AXIS_REF& _axisRef; // 轴引用,不持有所有权 + bool _prevEnable = false; // 上一周期的 Enable 值(边沿检测) +}; + + +} + + diff --git a/MotionLib/Src/Axis.cpp b/MotionLib/Src/Axis.cpp index 90c27a0..91ce899 100644 --- a/MotionLib/Src/Axis.cpp +++ b/MotionLib/Src/Axis.cpp @@ -14,14 +14,83 @@ namespace plcopen { Axis::Axis(UINT axisNo, std::string name) { - ref_.axisNo = axisNo; - ref_.axisName = std::move(name); - ref_.axis = this; + _ref.axisNo = axisNo; + _ref.axisName = std::move(name); + _ref.axis = this; } void Axis::cycle() { - + if (_errorStop) return; + + if (!_powerEnabled) return; + + switch (_state) { + case MC_AXIS_STATE::Disabled : + { + break; + } + + case MC_AXIS_STATE::Standstill : + { + break; + } + + case MC_AXIS_STATE::Stopping : + { + break; + } + + // 独立运动 + case MC_AXIS_STATE::DiscreteMotion : + { + break; + } + + // 持续运动 + case MC_AXIS_STATE::ContinuousMotion : + { + break; + } + + // 同步运动 + case MC_AXIS_STATE::SynchronizedMotion : + { + break; + } + + // 回零 + case MC_AXIS_STATE::Homing : + { + break; + } + + default: + { + break; + } + } +} + +void Axis::doPower(bool en) +{ + if(en) + { + _powerEnabled = true; + } + else { + _powerEnabled = false; + _activeCommand.type = MotionCommandType::None; + _velocity = 0.0; + _acceleration = 0.0; + _errorStop = false; + _state = MC_AXIS_STATE::Disabled; + } +} + +MC_AXIS_STATE Axis::readState() +{ + return _state; } } \ No newline at end of file diff --git a/MotionLib/Src/MC_Power.cpp b/MotionLib/Src/MC_Power.cpp new file mode 100644 index 0000000..d55709a --- /dev/null +++ b/MotionLib/Src/MC_Power.cpp @@ -0,0 +1,84 @@ +/** + * @file MC_Power.cpp + * @author + * @brief + * @version 0.1 + * @date 2026-07-03 + * + * @copyright Copyright (c) 2026 + * + */ + +#include "MC_Power.h" +#include "Types.h" + +namespace plcopen { + + MC_Power::MC_Power(AXIS_REF& axisRef) + : _axisRef(axisRef) { } + + void MC_Power::cycle() + { + if(_axisRef.axis == nullptr) + { + Error = true; + ErrorID = MC_ERROR_ID::mcInvalidParameter; + Status = false; + Busy = false; + return; + } + + Axis* axis = _axisRef.axis; + Error = false; + ErrorID = MC_ERROR_ID::mcNoError; + + // 上升沿 + if(Enable && !_prevEnable) + { + axis->doPower(true); + } + + // 下降沿 + if(!Enable && _prevEnable) + { + axis->doPower(false); + } + + // 在使能状态下持续更新轴状态 + if(Enable) + { + auto state = axis->readState(); + if(state == MC_AXIS_STATE::Standstill) + { + Busy = false; + Status = true; + } + else if(state == MC_AXIS_STATE::ErrorStop) + { + Busy = false; + Status = false; + Error = true; + ErrorID = MC_ERROR_ID::mcPowerFailure; + } + else + { + Status = false; + Busy = false; + } + + } + + // 保存前置状态 + _prevEnable = Enable; + + } + + +} + + + + + + + diff --git a/docs/design/Axis-Attributes.md b/docs/design/Axis-Attributes.md index 19c4a30..6ad92ad 100644 --- a/docs/design/Axis-Attributes.md +++ b/docs/design/Axis-Attributes.md @@ -2,11 +2,31 @@ `Axis` 是 PLCopen 运动控制库的核心仿真类,实现 IEC 61131-3 单轴状态机与运动学。 +## 设计决策 + +### 可见性:运动学属性为 `private` + +功能块不直接读写轴内部状态,而是通过 `AXIS_REF` 调用公开接口。这是 IEC 61131-3 的标准模式——轴是功能块黑盒: + +``` +功能块 (MC_MoveAbsolute) Axis +───────────────────── ───────────── + AXIS_REF → axis private: position_, velocity_, ... + axis.acceptCommand(cmd) ──────→ 只有 cycle() 能写运动学数据 + axis.readActualPosition() ←─── public: getter 只读 +``` + +> 运动控制线程定时调用 `cycle()` 独占写权限,避免数据竞争。 + +### 数值类型:浮点 `REAL` (double) + +遵循 IEC 61131-3 / PLCopen 规范,使用 `double` 表示物理量(mm、°/s 等)。`double` 有 52 位尾数(约 15 位十进制有效数字),以 10ms 周期运行 1 小时,累积的浮点舍入误差在 $10^{-10}$ 量级,工程上可完全忽略。若未来对接真实驱动器,在数据交换层做脉冲数换算即可。 + --- ## 1. 当前已实现 -### 1.1 轴标识与自引用 — `AXIS_REF ref_` +### 1.1 轴标识与自引用 — `AXIS_REF _ref` (private) | 字段 | 类型 | 说明 | |------|------|------| @@ -14,47 +34,54 @@ | `axisName` | `std::string` | 轴名称,默认 `"Axis1"` | | `axis` | `Axis*` | 指向自身实例的指针,供功能块间接访问轴对象 | -> `AXIS_REF` 在 IEC 61131-3 中是 `VAR_IN_OUT` 参数,本实现中构造时自绑定。 +### 1.2 轴状态 — `MC_AXIS_STATE _state` (private) -### 1.2 轴状态 — `MC_AXIS_STATE state_` +当前状态,初始值 `Disabled`。新增辅助状态 `_resultingState` 用于记录指令完成后的目标状态。 -当前状态机状态,初始值为 `Disabled`。可能的取值: +状态转换由 `cycle()` 方法驱动(逻辑待实现)。 -``` -Disabled → Standstill → DiscreteMotion / ContinuousMotion / Homing - → Stopping - ErrorStop - SynchronizedMotion (预留) -``` +### 1.3 运动学属性 (private) ✅ -状态转换由 `cycle()` 方法驱动(尚未实现)。 - -### 1.3 构造函数 - -```cpp -explicit Axis(UINT axisNo = 1, std::string name = "Axis1"); -``` - -初始化 `ref_` 并自绑定 `axis` 指针,`state_` 默认 `Disabled`。 - ---- - -## 2. 必须扩展的属性(待实现) - -按照 PLCopen 规范,完整的轴模型还需以下属性。 - -### 2.1 运动学属性 +`cycle()` 独占写入;外部通过 `readXxx()` 只读访问。 | 属性 | 类型 | 说明 | |------|------|------| -| `position_` | `REAL` | 当前指令位置 | -| `actualPosition_` | `REAL` | 当前实际位置 | -| `velocity_` | `REAL` | 当前速度 | -| `actualVelocity_` | `REAL` | 实际速度 | -| `acceleration_` | `REAL` | 当前加速度 | -| `deceleration_` | `REAL` | 当前减速度 | +| `_position` | `REAL` | 当前指令位置 | +| `_actualPosition` | `REAL` | 当前实际位置(含跟随误差仿真) | +| `_velocity` | `REAL` | 当前指令速度 | +| `_actualVelocity` | `REAL` | 当前实际速度 | +| `_acceleration` | `REAL` | 当前指令加速度 | +| `_actualAcceleration` | `REAL` | 当前实际加速度 | +| `_deceleration` | `REAL` | 减速度(尚未添加) | -### 2.2 活动指令 — `MotionCommand activeCommand_` +### 1.4 功率与使能 (private) ✅ + +| 属性 | 类型 | 说明 | +|------|------|------| +| `_powerEnabled` | `BOOL` | 功率级是否使能 | +| `_homed` | `BOOL` | 是否已完成回零 | + +### 1.5 错误管理 (private) ✅ + +| 属性 | 类型 | 说明 | +|------|------|------| +| `_errorId` | `MC_ERROR_ID` | 当前错误码,`mcNoError` 表示正常 | +| `_errorStop` | `BOOL` | 是否触发错误停止 | + +### 1.6 构造函数 & cycle() (public) + +```cpp +explicit Axis(UINT axisNo = 1, std::string name = "Axis1"); +void cycle(); // 骨架已生成,逻辑待实现 +``` + +初始化 `_ref` 并自绑定 `axis` 指针,`_state` 默认 `Disabled`。 + +--- + +## 2. 待实现的属性与方法 + +### 2.1 活动指令 — `MotionCommand _activeCommand` (private) 当前正在执行的运动指令参数快照,类型为 `MotionCommand`: @@ -69,61 +96,69 @@ explicit Axis(UINT axisNo = 1, std::string name = "Axis1"); | `homePosition` | `REAL` | 回零位置 | | `resultingState` | `MC_AXIS_STATE` | 指令完成后要进入的状态 | -### 2.3 功率与使能 +### 2.2 待补充的运动学属性 -| 属性 | 类型 | 说明 | -|------|------|------| -| `powerEnabled_` | `BOOL` | 功率级是否使能 | -| `homed_` | `BOOL` | 是否已完成回零 | +| 属性 | 说明 | +|------|------| +| `_deceleration` | 减速度(已有 `_acceleration`,缺独立减速参数) | -### 2.4 错误管理 - -| 属性 | 类型 | 说明 | -|------|------|------| -| `errorId_` | `MC_ERROR_ID` | 当前错误码,`mcNoError` 表示正常 | -| `errorStop_` | `BOOL` | 是否触发错误停止 | - -### 2.5 轴状态机方法 — `cycle()` +### 2.3 轴状态机方法 — `cycle()` (public, 实现待完成) ```cpp -void cycle(REAL deltaTime); +void cycle(); ``` -每周期由应用层调用,执行: -1. 根据 `activeCommand_` 计算运动学输出 -2. 更新 `position_` / `velocity_` +每周期由运动控制线程调用,待实现逻辑: +1. 根据 `_activeCommand` 计算运动学输出 +2. 更新 `_position` / `_velocity` 等 3. 评估状态转换条件 4. 在离散运动到达目标时自动切换到 `Standstill` -### 2.6 功能块交互接口 +### 2.4 功能块交互接口 (public, 全部待实现) -| 方法 | 说明 | -|------|------| -| `acceptCommand(const MotionCommand&)` | 接收功能块下发的运动指令 | -| `doPower(BOOL enable)` | 功率使能/去使能 | -| `doStop()` / `doHalt()` | 停止 / 暂停 | -| `doReset()` | 从 ErrorStop 恢复 | -| `doHome(REAL homePos)` | 发起回零 | -| `readStatus()` | 读取当前状态快照 | -| `readError()` | 读取当前错误码 | +| 方法 | 可见性 | 说明 | +|------|--------|------| +| `acceptCommand(const MotionCommand&)` | public | 接收功能块下发的运动指令 | +| `doPower(BOOL enable)` | public | 功率使能/去使能 | +| `doStop()` / `doHalt()` | public | 停止 / 暂停 | +| `doReset()` | public | 从 ErrorStop 恢复 | +| `doHome(REAL homePos)` | public | 发起回零 | +| `readStatus()` | public | 读取当前状态快照 | +| `readActualPosition()` | public | 读取实际位置 | +| `readActualVelocity()` | public | 读取实际速度 | +| `readError()` | public | 读取当前错误码 | --- -## 3. 完整属性清单(含待实现) +## 3. 完整属性清单 ``` + private + ─────── Axis -├── ref_ (AXIS_REF) ✅ 已实现 -├── state_ (MC_AXIS_STATE) ✅ 已实现 -├── position_ (REAL) ⬜ 位置 -├── actualPosition_ (REAL) ⬜ 实际位置 -├── velocity_ (REAL) ⬜ 速度 -├── actualVelocity_ (REAL) ⬜ 实际速度 -├── acceleration_ (REAL) ⬜ 加速度 -├── deceleration_ (REAL) ⬜ 减速度 -├── activeCommand_ (MotionCommand) ⬜ 活动指令 -├── powerEnabled_ (BOOL) ⬜ 功率使能 -├── homed_ (BOOL) ⬜ 回零完成 -├── errorId_ (MC_ERROR_ID) ⬜ 错误码 -└── errorStop_ (BOOL) ⬜ 错误停止 +├── _ref (AXIS_REF) ✅ +├── _state (MC_AXIS_STATE) ✅ +├── _resultingState (MC_AXIS_STATE) ✅ 指令完成目标状态 +├── _position (REAL) ✅ +├── _actualPosition (REAL) ✅ +├── _velocity (REAL) ✅ +├── _actualVelocity (REAL) ✅ +├── _acceleration (REAL) ✅ +├── _actualAcceleration (REAL) ✅ +├── _deceleration (REAL) ⬜ 待添加 +├── _powerEnabled (BOOL) ✅ +├── _homed (BOOL) ✅ +├── _errorId (MC_ERROR_ID) ✅ +├── _errorStop (BOOL) ✅ +└── _activeCommand (MotionCommand) ⬜ 活动指令 + + public + ────── + Axis(axisNo, name) ✅ + cycle() 🟡 骨架已就绪 + acceptCommand(cmd) ⬜ + doPower / doStop / doHalt ⬜ + doReset / doHome ⬜ + readStatus / readActualPosition ⬜ + readActualVelocity / readError ⬜ ``` diff --git a/docs/design/MC_Power.md b/docs/design/MC_Power.md new file mode 100644 index 0000000..d5e8ed4 --- /dev/null +++ b/docs/design/MC_Power.md @@ -0,0 +1,185 @@ +# MC_Power 功能块设计 + +`MC_Power` 是 PLCopen 运动控制库的基础功能块,控制轴的功率级使能与去使能。 + +--- + +## 1. IEC 61131-3 接口定义 + +``` +FUNCTION_BLOCK MC_Power + VAR_IN_OUT + Axis : AXIS_REF; (* 轴引用 *) + END_VAR + VAR_INPUT + Enable : BOOL; (* TRUE=使能, FALSE=去使能 *) + Enable_Positive : BOOL; (* 允许正向运动,本库保留 *) + Enable_Negative : BOOL; (* 允许负向运动,本库保留 *) + END_VAR + VAR_OUTPUT + Status : BOOL; (* 轴已使能且就绪 *) + Busy : BOOL; (* 功能块正在处理中 *) + Error : BOOL; (* 错误标志 *) + ErrorID : MC_ERROR_ID;(* 错误码 *) + END_VAR +``` + +## 2. C++ 类实现 + +### 2.1 类声明 — `MotionLib/Inc/MC_Power.h` + +```cpp +#pragma once +#include "Types.h" + +namespace plcopen { + +class MC_Power { +public: + /** + * \brief 构造 MC_Power 实例 + * \param axisRef 轴引用 (VAR_IN_OUT) + */ + explicit MC_Power(AXIS_REF& axisRef); + + /** + * \brief 周期调用,执行功率控制逻辑 + * + * 上位机每个运动控制周期调用一次。 + */ + void cycle(); + + // --- 输入参数 (VAR_INPUT) --- + BOOL Enable = false; + BOOL Enable_Positive = true; // 本库默认允许 + BOOL Enable_Negative = true; // 本库默认允许 + + // --- 输出参数 (VAR_OUTPUT) --- + BOOL Status = false; + BOOL Busy = false; + BOOL Error = false; + MC_ERROR_ID ErrorID = MC_ERROR_ID::mcNoError; + +private: + AXIS_REF& _axisRef; // 轴引用,不持有所有权 + bool _prevEnable = false; // 上一周期的 Enable 值(边沿检测) +}; + +} // namespace plcopen +``` + +### 2.2 实现 — `MotionLib/Src/MC_Power.cpp` + +```cpp +#include "MC_Power.h" +#include "Axis.h" + +namespace plcopen { + +MC_Power::MC_Power(AXIS_REF& axisRef) + : _axisRef(axisRef) {} + +void MC_Power::cycle() { + // ① 有效性检查 + if (_axisRef.axis == nullptr) { + Error = true; + ErrorID = MC_ERROR_ID::mcInvalidParameter; + Status = false; + Busy = false; + return; + } + + Axis* axis = _axisRef.axis; + Error = false; + ErrorID = MC_ERROR_ID::mcNoError; + + // ② Enable 上升沿 — 使能 + if (Enable && !_prevEnable) { + axis->doPower(true); + Busy = true; + } + + // ③ Enable 下降沿 — 去使能 + if (!Enable && _prevEnable) { + axis->doPower(false); + Busy = false; + } + + // ④ 持续使能状态 — 等待轴进入 Standstill + if (Enable) { + MC_AXIS_STATE state = axis->readState(); + + if (state == MC_AXIS_STATE::Standstill) { + Busy = false; + Status = true; // 使能成功 + } else if (state == MC_AXIS_STATE::ErrorStop) { + Busy = false; + Status = false; + Error = true; + ErrorID = MC_ERROR_ID::mcPowerFailure; + } + } else { + // 去使能后 + Status = false; + Busy = false; + } + + // ⑤ 保存前置状态 + _prevEnable = Enable; +} + +} // namespace plcopen +``` + +## 3. 行为时序 + +``` +周期 | Enable | axis 状态 | Status | Busy | 说明 +─────┼────────┼───────────────┼────────┼──────┼──────────────── + 0 | FALSE | Disabled | FALSE | FALSE| 初始状态 + 1 | TRUE ↑ | Disabled | FALSE | TRUE | 上升沿,doPower(true) + 2 | TRUE | Disabled | FALSE | TRUE | cycle() 待处理 + 3 | TRUE | Standstill | TRUE | FALSE| 使能完成 ✅ + 4 | FALSE ↓| Standstill | FALSE | FALSE| 下降沿,doPower(false) + 5 | FALSE | Disabled | FALSE | FALSE| 去使能完成 +``` + +## 4. 与 Axis 的交互 + +``` +MC_Power Axis +──────── ──── + cycle() + │ + ├── Enable↑ → axis->doPower(true) ──→ _powerEnabled = true + │ _state 将在 cycle() 中切换 + │ + ├── Enable↓ → axis->doPower(false) ──→ _powerEnabled = false + │ _state → Disabled + │ + └── axis->readState() ←────────────── 读取 _state + axis->readError() ←───────────── 读取 _errorId +``` + +## 5. Axis 侧需要的新方法 + +```cpp +// Axis.h — 新增方法 + +class Axis { +public: + void doPower(BOOL enable); // 功率使能控制 + MC_AXIS_STATE readState() const; // 读当前状态 + MC_ERROR_ID readError() const; // 读当前错误码 +}; +``` + +## 6. 设计要点 + +| 要点 | 说明 | +|------|------| +| 不持有 Axis | `_axisRef` 是引用,MC_Power 不负责轴的生命周期 | +| 上升/下降沿检测 | `_prevEnable` 确保 doPower() 只在边沿调用一次 | +| Busy 的语义 | 从 Enable↑ 到 Status=TRUE 之间为 Busy | +| Error 路径 | 轴进入 ErrorStop 时,Status→FALSE,Error→TRUE | +| Enable_Positive/Negative | IEC 完整规范字段,本库保留输入但暂不实现逻辑 |