Compare commits
7 Commits
34d2b3c629
...
bcd94b3307
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bcd94b3307 | ||
|
|
b5382163e0 | ||
|
|
6aa98241ef | ||
|
|
12310de782 | ||
|
|
3df459cb33 | ||
|
|
4c1ec24be8 | ||
|
|
2d9bd1d4d7 |
599
docs/notes/接口调用履历.md
Normal file
599
docs/notes/接口调用履历.md
Normal file
@@ -0,0 +1,599 @@
|
|||||||
|
# 接口调用履历
|
||||||
|
|
||||||
|
本文档用于快速梳理当前项目里“前端请求 -> WASM 导出函数 -> C++ 分发层 -> 业务实现文件”的执行链路,便于对照源码阅读。
|
||||||
|
|
||||||
|
## 1. 建议先看的总入口
|
||||||
|
|
||||||
|
建议按下面顺序看文件:
|
||||||
|
|
||||||
|
1. `src/main.cpp`
|
||||||
|
说明这个工程最终是一个 WASM 可执行模块,`main` 主要用于本地构建提示。
|
||||||
|
2. `src/smart_json_wrapper.cpp`
|
||||||
|
这里是所有 WASM 导出函数的真正入口。
|
||||||
|
3. `include/KinematicsWebAPI.h`
|
||||||
|
业务接口类声明,能看见分发结构。
|
||||||
|
4. `src/api/KinematicsWebAPI.Core.cpp`
|
||||||
|
业务 JSON 请求的总分发入口。
|
||||||
|
5. `src/api/KinematicsWebAPI.*Commands.cpp`
|
||||||
|
按功能拆开的命令处理层。
|
||||||
|
6. 对应底层业务文件
|
||||||
|
机器人看 `src/Robot.cpp`、`src/RobotManager.cpp`
|
||||||
|
SPC 看 `src/spc_core.cpp`
|
||||||
|
四连杆看 `src/FourBarMechanism/`
|
||||||
|
四足看 `src/QuadrupedRobotSimulation/`
|
||||||
|
|
||||||
|
## 2. 顶层 WASM 导出接口
|
||||||
|
|
||||||
|
### 2.1 业务接口导出
|
||||||
|
|
||||||
|
前端主调的是这两个导出函数:
|
||||||
|
|
||||||
|
- `init_func`
|
||||||
|
- `func`
|
||||||
|
|
||||||
|
调用链如下:
|
||||||
|
|
||||||
|
```text
|
||||||
|
JS/HTML
|
||||||
|
-> public/wasm/smart_api_wrapper.js
|
||||||
|
-> WASM 导出函数 init_func / func
|
||||||
|
-> src/smart_json_wrapper.cpp
|
||||||
|
-> KinematicsWebAPI
|
||||||
|
-> src/api/KinematicsWebAPI.Core.cpp
|
||||||
|
-> 各功能 Commands 文件
|
||||||
|
-> 底层业务实现
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2.2 智能数学/测试接口导出
|
||||||
|
|
||||||
|
这一套和业务接口是并行的另一条线:
|
||||||
|
|
||||||
|
- `smart_process_json`
|
||||||
|
- `smart_get_function_list`
|
||||||
|
- `smart_get_function_info`
|
||||||
|
- `smart_test_match`
|
||||||
|
- `smart_get_version`
|
||||||
|
|
||||||
|
调用链如下:
|
||||||
|
|
||||||
|
```text
|
||||||
|
JS/HTML
|
||||||
|
-> public/wasm/smart_api_wrapper.js
|
||||||
|
-> smart_* 导出函数
|
||||||
|
-> src/smart_json_wrapper.cpp
|
||||||
|
-> SmartJsonProcessor / FunctionRegistry
|
||||||
|
-> math_utils / 注册的测试函数
|
||||||
|
```
|
||||||
|
|
||||||
|
这一套主要是“数学函数演示 + 智能参数匹配”,不走 `KinematicsWebAPI`。
|
||||||
|
|
||||||
|
## 3. 业务总分发链路
|
||||||
|
|
||||||
|
### 3.1 初始化
|
||||||
|
|
||||||
|
`src/smart_json_wrapper.cpp` 中:
|
||||||
|
|
||||||
|
- `init_func()`
|
||||||
|
- 首次调用时创建全局单例 `global_server = new KinematicsWebAPI()`
|
||||||
|
- `KinematicsWebAPI` 构造函数里会继续调用 `KinematicsWebAPI::init_func()`
|
||||||
|
|
||||||
|
`src/api/KinematicsWebAPI.Core.cpp` 中:
|
||||||
|
|
||||||
|
- `KinematicsWebAPI::init_func()`
|
||||||
|
- 从 `include/URDFStrings.h` 读取默认 URDF
|
||||||
|
- 调用 `RobotManager::initRobot(...)`
|
||||||
|
- 默认会初始化两个机器人实例
|
||||||
|
|
||||||
|
### 3.2 请求处理
|
||||||
|
|
||||||
|
`src/smart_json_wrapper.cpp`
|
||||||
|
|
||||||
|
- `func(const char* json_request)`
|
||||||
|
- 把前端 JSON 字符串转成 `std::string`
|
||||||
|
- 调 `global_server->func(request_str)`
|
||||||
|
|
||||||
|
`src/api/KinematicsWebAPI.Core.cpp`
|
||||||
|
|
||||||
|
- `KinematicsWebAPI::func(std::string sanitized_body)`
|
||||||
|
- 解析 JSON
|
||||||
|
- 取出 `msg / req_code / req_from / req_cmd / req_param`
|
||||||
|
- 调 `dispatchCommand(req_cmd, req_param)`
|
||||||
|
- 最后统一封装为 `utils::create_api_response(...)`
|
||||||
|
|
||||||
|
- `KinematicsWebAPI::dispatchCommand(...)`
|
||||||
|
- 机器人类命令 -> `handleRobotCommand(...)`
|
||||||
|
- SPC -> `handleSpcCommand(...)`
|
||||||
|
- 四连杆 -> `handleFourBarCommand(...)`
|
||||||
|
- 四足 -> `handleQuadrupedCommand(...)`
|
||||||
|
|
||||||
|
## 4. 机器人相关接口履历
|
||||||
|
|
||||||
|
机器人相关命令都在:
|
||||||
|
|
||||||
|
- `src/api/KinematicsWebAPI.RobotCommands.cpp`
|
||||||
|
|
||||||
|
底层核心文件:
|
||||||
|
|
||||||
|
- `src/RobotManager.cpp`
|
||||||
|
- `src/Robot.cpp`
|
||||||
|
|
||||||
|
### 4.1 `Cmd_InitRobot`
|
||||||
|
|
||||||
|
调用履历:
|
||||||
|
|
||||||
|
```text
|
||||||
|
func
|
||||||
|
-> KinematicsWebAPI::dispatchCommand
|
||||||
|
-> KinematicsWebAPI::handleRobotCommand
|
||||||
|
-> Cmd_InitRobot 分支
|
||||||
|
-> utils::base64_to_urdf
|
||||||
|
-> utils::validate_urdf_base64
|
||||||
|
-> RobotManager::initRobot
|
||||||
|
-> Robot::initRobot
|
||||||
|
```
|
||||||
|
|
||||||
|
底层细节:
|
||||||
|
|
||||||
|
- `RobotManager::initRobot`
|
||||||
|
- 创建 `std::shared_ptr<Robot>`
|
||||||
|
- 调 `robot->initRobot(urdf_robot)`
|
||||||
|
- 生成或使用 `robot_uuid`
|
||||||
|
- 计算 URDF 哈希
|
||||||
|
- 调 `_validateKinematics(robot)` 做基本正解验证
|
||||||
|
- 存入 `_kinematics_table`
|
||||||
|
|
||||||
|
- `Robot::initRobot`
|
||||||
|
- `kdl_parser::treeFromString(...)`
|
||||||
|
- `tree.getChain("base", "tool0", kinematicChain)`
|
||||||
|
- `parseJointChildLinkUuidsFromUrdf(...)`
|
||||||
|
- 初始化 FK / IK 求解器
|
||||||
|
|
||||||
|
### 4.2 `Cmd_GetRobot`
|
||||||
|
|
||||||
|
调用履历:
|
||||||
|
|
||||||
|
```text
|
||||||
|
func
|
||||||
|
-> dispatchCommand
|
||||||
|
-> handleRobotCommand
|
||||||
|
-> Cmd_GetRobot
|
||||||
|
-> RobotManager::getRobot
|
||||||
|
-> Robot::isInitialized / Robot::getNumberOfJoints
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4.3 `Cmd_RemoveRobot`
|
||||||
|
|
||||||
|
调用履历:
|
||||||
|
|
||||||
|
```text
|
||||||
|
func
|
||||||
|
-> dispatchCommand
|
||||||
|
-> handleRobotCommand
|
||||||
|
-> Cmd_RemoveRobot
|
||||||
|
-> RobotManager::removeRobot
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4.4 `Cmd_ListRobots`
|
||||||
|
|
||||||
|
调用履历:
|
||||||
|
|
||||||
|
```text
|
||||||
|
func
|
||||||
|
-> dispatchCommand
|
||||||
|
-> handleRobotCommand
|
||||||
|
-> Cmd_ListRobots
|
||||||
|
-> RobotManager::listRobots
|
||||||
|
-> detail=true 时内部还会调 RobotManager::_validateKinematics
|
||||||
|
-> Robot::calculateFK_TCP
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4.5 `Cmd_Kinematics_forward_pose_str`
|
||||||
|
|
||||||
|
用途:
|
||||||
|
|
||||||
|
- 输入一组 6 轴关节角
|
||||||
|
- 输出 TCP 位姿
|
||||||
|
|
||||||
|
调用履历:
|
||||||
|
|
||||||
|
```text
|
||||||
|
func
|
||||||
|
-> dispatchCommand
|
||||||
|
-> handleRobotCommand
|
||||||
|
-> Cmd_Kinematics_forward_pose_str
|
||||||
|
-> RobotManager::getRobot
|
||||||
|
-> Robot::parseJointString
|
||||||
|
-> Robot::calculateFK_TCP
|
||||||
|
```
|
||||||
|
|
||||||
|
`Robot::calculateFK_TCP` 内部主要做:
|
||||||
|
|
||||||
|
- 关节数组转 `KDL::JntArray`
|
||||||
|
- `fkSolver->JntToCart(...)`
|
||||||
|
- 取出位置和四元数
|
||||||
|
|
||||||
|
### 4.6 `Cmd_Kinematics_forward_all_joints`
|
||||||
|
|
||||||
|
用途:
|
||||||
|
|
||||||
|
- 输入一帧或多帧关节角列表
|
||||||
|
- 输出每个关节节点的位姿,组织成前端使用的 `OPERATION.frames[].objStates`
|
||||||
|
|
||||||
|
调用履历:
|
||||||
|
|
||||||
|
```text
|
||||||
|
func
|
||||||
|
-> dispatchCommand
|
||||||
|
-> handleRobotCommand
|
||||||
|
-> Cmd_Kinematics_forward_all_joints
|
||||||
|
-> RobotManager::getRobot
|
||||||
|
-> Robot::handleKinematicsForwardAllJoints
|
||||||
|
-> Robot::parseJointListString
|
||||||
|
-> Robot::kinematicsForwardAllJointsList
|
||||||
|
-> Robot::forwardKinematics
|
||||||
|
-> Robot::getJointUuidByIndex
|
||||||
|
```
|
||||||
|
|
||||||
|
补充说明:
|
||||||
|
|
||||||
|
- `Robot::handleKinematicsForwardAllJoints(...)`
|
||||||
|
- 遍历每一帧关节角
|
||||||
|
- 每帧都调一次 `kinematicsForwardAllJointsList(...)`
|
||||||
|
- `Robot::kinematicsForwardAllJointsList(...)`
|
||||||
|
- 调 `forwardKinematics(...)`
|
||||||
|
- 把 6 个关节的位姿打包成 `objStates`
|
||||||
|
|
||||||
|
### 4.7 `Cmd_Kinematics_inverse_pose_str_NoDifference`
|
||||||
|
|
||||||
|
用途:
|
||||||
|
|
||||||
|
- 输入一个或多个姿态点
|
||||||
|
- 不做插补,直接逐点逆解
|
||||||
|
|
||||||
|
调用履历:
|
||||||
|
|
||||||
|
```text
|
||||||
|
func
|
||||||
|
-> dispatchCommand
|
||||||
|
-> handleRobotCommand
|
||||||
|
-> Cmd_Kinematics_inverse_pose_str_NoDifference
|
||||||
|
-> RobotManager::getRobot
|
||||||
|
-> Robot::inversePoseStrNoDifference
|
||||||
|
-> Robot::parsePoseString
|
||||||
|
-> Robot::parseJointString
|
||||||
|
-> Robot::inverse(vector)
|
||||||
|
-> Robot::calculateIK_LMA
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4.8 `Cmd_Kinematics_inverse_pose_str`
|
||||||
|
|
||||||
|
用途:
|
||||||
|
|
||||||
|
- 输入多个姿态点
|
||||||
|
- 相邻姿态间先自动插补轨迹,再逐点逆解
|
||||||
|
|
||||||
|
调用履历:
|
||||||
|
|
||||||
|
```text
|
||||||
|
func
|
||||||
|
-> dispatchCommand
|
||||||
|
-> handleRobotCommand
|
||||||
|
-> Cmd_Kinematics_inverse_pose_str
|
||||||
|
-> RobotManager::getRobot
|
||||||
|
-> Robot::inversePoseStr
|
||||||
|
-> Robot::parsePoseString
|
||||||
|
-> Robot::parseJointString
|
||||||
|
-> Robot::calculateAutoSteps
|
||||||
|
-> Robot::trajectoryPlanning
|
||||||
|
-> Robot::inverse(vector)
|
||||||
|
-> Robot::calculateIK_LMA
|
||||||
|
```
|
||||||
|
|
||||||
|
关键点:
|
||||||
|
|
||||||
|
- `calculateAutoSteps(...)` 根据位置差和姿态差自动算插补点数
|
||||||
|
- `trajectoryPlanning(...)` 调 KDL 轨迹插补
|
||||||
|
- 每个插补点再进入 `inverse(...)`
|
||||||
|
|
||||||
|
### 4.9 `Cmd_Kinematics_inverse_pose_str_2PSteps`
|
||||||
|
|
||||||
|
用途:
|
||||||
|
|
||||||
|
- 只取两个姿态点
|
||||||
|
- 按指定 `steps` 做轨迹插补
|
||||||
|
|
||||||
|
调用履历:
|
||||||
|
|
||||||
|
```text
|
||||||
|
func
|
||||||
|
-> dispatchCommand
|
||||||
|
-> handleRobotCommand
|
||||||
|
-> Cmd_Kinematics_inverse_pose_str_2PSteps
|
||||||
|
-> RobotManager::getRobot
|
||||||
|
-> Robot::inversePoseStr2PSteps
|
||||||
|
-> Robot::parsePoseString
|
||||||
|
-> Robot::parseJointString
|
||||||
|
-> Robot::trajectoryPlanning(..., steps)
|
||||||
|
-> Robot::inverse(vector)
|
||||||
|
-> Robot::calculateIK_LMA
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4.10 `Cmd_SelectCraftTree` / `Cmd_AddOperationTree`
|
||||||
|
|
||||||
|
这两个目前是轻量占位接口。
|
||||||
|
|
||||||
|
调用履历:
|
||||||
|
|
||||||
|
```text
|
||||||
|
func
|
||||||
|
-> dispatchCommand
|
||||||
|
-> handleRobotCommand
|
||||||
|
-> 直接在 KinematicsWebAPI.RobotCommands.cpp 内拼装返回 JSON
|
||||||
|
```
|
||||||
|
|
||||||
|
当前没有继续下钻到独立业务类。
|
||||||
|
|
||||||
|
## 5. SPC 接口履历
|
||||||
|
|
||||||
|
命令文件:
|
||||||
|
|
||||||
|
- `src/api/KinematicsWebAPI.SpcCommands.cpp`
|
||||||
|
|
||||||
|
底层文件:
|
||||||
|
|
||||||
|
- `include/spc_core.h`
|
||||||
|
- `src/spc_core.cpp`
|
||||||
|
|
||||||
|
### 5.1 `Cmd_Spc`
|
||||||
|
|
||||||
|
调用履历:
|
||||||
|
|
||||||
|
```text
|
||||||
|
func
|
||||||
|
-> dispatchCommand
|
||||||
|
-> handleSpcCommand
|
||||||
|
-> SpcCalculator::Spc
|
||||||
|
-> SpcTestData::FromJson
|
||||||
|
-> SpcCalculator::CalculateXR
|
||||||
|
-> SpcCalculator::CalculateXS
|
||||||
|
-> SpcCalculator::CalculateCpk
|
||||||
|
-> SpcCalculator::RoundSpcData
|
||||||
|
-> SpcResultJson::ToJson
|
||||||
|
```
|
||||||
|
|
||||||
|
底层职责分布:
|
||||||
|
|
||||||
|
- `SpcTestData::FromJson`
|
||||||
|
- 从请求中读取 `n / k / usl / lsl / x`
|
||||||
|
- `CalculateXR`
|
||||||
|
- 算 X-R 控制图
|
||||||
|
- `CalculateXS`
|
||||||
|
- 算 X-S 控制图
|
||||||
|
- `CalculateCpk`
|
||||||
|
- 算过程能力指数和直方图数据
|
||||||
|
- `RoundSpcData`
|
||||||
|
- 做统一小数位保留
|
||||||
|
|
||||||
|
## 6. 四连杆接口履历
|
||||||
|
|
||||||
|
命令文件:
|
||||||
|
|
||||||
|
- `src/api/KinematicsWebAPI.FourBarCommands.cpp`
|
||||||
|
|
||||||
|
底层文件:
|
||||||
|
|
||||||
|
- `include/FourBarMechanism/CrankSliderMechanism.h`
|
||||||
|
- `src/FourBarMechanism/CrankSliderMechanism.cpp`
|
||||||
|
|
||||||
|
### 6.1 `Cmd_FourBar_CrankSlider`
|
||||||
|
|
||||||
|
调用履历:
|
||||||
|
|
||||||
|
```text
|
||||||
|
func
|
||||||
|
-> dispatchCommand
|
||||||
|
-> handleFourBarCommand
|
||||||
|
-> createCrankSliderMechanism
|
||||||
|
-> CrankSliderMechanism::setL_AB / setL_BS / setS_OFS
|
||||||
|
-> CrankSliderMechanism::validateParameters
|
||||||
|
-> CrankSliderMechanism::calculate
|
||||||
|
-> CrankSliderMechanism::getTrajectoryPoints
|
||||||
|
-> CrankSliderMechanism::getSliderTrajectory
|
||||||
|
```
|
||||||
|
|
||||||
|
说明:
|
||||||
|
|
||||||
|
- 命令层负责把参数取出来、调机制对象、再把结果组装成 JSON
|
||||||
|
- 真正的机构计算在 `src/FourBarMechanism/CrankSliderMechanism.cpp`
|
||||||
|
|
||||||
|
## 7. 四足机器人接口履历
|
||||||
|
|
||||||
|
命令文件:
|
||||||
|
|
||||||
|
- `src/api/KinematicsWebAPI.QuadrupedCommands.cpp`
|
||||||
|
|
||||||
|
中间帮助层:
|
||||||
|
|
||||||
|
- `include/QuadrupedRobotSimulation/KinematicsHelper.h`
|
||||||
|
- `src/QuadrupedRobotSimulation/KinematicsHelper.cpp`
|
||||||
|
|
||||||
|
底层实现文件:
|
||||||
|
|
||||||
|
- `src/QuadrupedRobotSimulation/KinematicsSimulation.cpp`
|
||||||
|
- `src/QuadrupedRobotSimulation/KinematicsReverse.cpp`
|
||||||
|
- `src/QuadrupedRobotSimulation/CompleteJsonExporter.cpp`
|
||||||
|
- `src/QuadrupedRobotSimulation/RobotConfig.cpp`
|
||||||
|
- `src/QuadrupedRobotSimulation/SharedGeometry.cpp`
|
||||||
|
|
||||||
|
### 7.1 `Cmd_QuadrupedRobot_CalculateAllPointsFromMotorAngles`
|
||||||
|
|
||||||
|
用途:
|
||||||
|
|
||||||
|
- 输入电机角
|
||||||
|
- 反推出各关键点坐标
|
||||||
|
|
||||||
|
调用履历:
|
||||||
|
|
||||||
|
```text
|
||||||
|
func
|
||||||
|
-> dispatchCommand
|
||||||
|
-> handleQuadrupedCommand
|
||||||
|
-> KinematicsHelper::QuadrupedRobot_CalculateAllPointsFromMotorAngles
|
||||||
|
-> KinematicsHelper::RobotGaitDataManagerFromJson
|
||||||
|
-> ReverseKinematicsCalculator
|
||||||
|
-> ReverseKinematicsCalculator::CalculateAllPointsFromMotorAnglesJsonStr
|
||||||
|
-> json::parse
|
||||||
|
```
|
||||||
|
|
||||||
|
说明:
|
||||||
|
|
||||||
|
- 数据管理入口在 `RobotGaitDataManagerFromJson(...)`
|
||||||
|
- 反解主逻辑在 `src/QuadrupedRobotSimulation/KinematicsReverse.cpp`
|
||||||
|
|
||||||
|
### 7.2 `Cmd_QuadrupedRobot_PerformForwardKinematics`
|
||||||
|
|
||||||
|
用途:
|
||||||
|
|
||||||
|
- 输入步态/系统参数
|
||||||
|
- 正向生成四足机器人整段步态轨迹
|
||||||
|
|
||||||
|
调用履历:
|
||||||
|
|
||||||
|
```text
|
||||||
|
func
|
||||||
|
-> dispatchCommand
|
||||||
|
-> handleQuadrupedCommand
|
||||||
|
-> KinematicsHelper::QuadrupedRobot_PerformForwardKinematics
|
||||||
|
-> KinematicsHelper::RobotGaitDataManagerFromJson
|
||||||
|
-> QuadrupedRobotConfiguration
|
||||||
|
-> QuadrupedRobotSimulation
|
||||||
|
-> QuadrupedRobotSimulation::CalculateAllTrajectoriesJsonString
|
||||||
|
-> CompleteJsonExporter::ExportCompleteDataJsonString
|
||||||
|
-> json::parse
|
||||||
|
```
|
||||||
|
|
||||||
|
说明:
|
||||||
|
|
||||||
|
- 配置拼装在 `RobotConfig.cpp`
|
||||||
|
- 轨迹生成主体在 `KinematicsSimulation.cpp`
|
||||||
|
- 最终 JSON 打包在 `CompleteJsonExporter.cpp`
|
||||||
|
|
||||||
|
## 8. 智能数学接口履历
|
||||||
|
|
||||||
|
这部分不是机器人业务,但也常会看到。
|
||||||
|
|
||||||
|
核心文件:
|
||||||
|
|
||||||
|
- `src/smart_json_wrapper.cpp`
|
||||||
|
- `include/smart_json_wrapper.h`
|
||||||
|
- `src/math_utils.cpp`
|
||||||
|
|
||||||
|
### 8.1 `smart_process_json`
|
||||||
|
|
||||||
|
调用履历:
|
||||||
|
|
||||||
|
```text
|
||||||
|
smart_process_json
|
||||||
|
-> SmartJsonProcessor::processRequest
|
||||||
|
-> 解析 req_cmd
|
||||||
|
-> FunctionRegistry::getFunction
|
||||||
|
-> SmartJsonProcessor::parseJsonParams
|
||||||
|
-> FunctionRegistry::smartMatchParams
|
||||||
|
-> FunctionInfo::validateParams
|
||||||
|
-> function handler lambda
|
||||||
|
-> math_utils 中的 add/subtract/multiply/divide/fibonacci 等
|
||||||
|
```
|
||||||
|
|
||||||
|
### 8.2 `smart_get_function_list`
|
||||||
|
|
||||||
|
调用履历:
|
||||||
|
|
||||||
|
```text
|
||||||
|
smart_get_function_list
|
||||||
|
-> SmartJsonProcessor::getAvailableFunctions
|
||||||
|
-> FunctionRegistry::getAllFunctions
|
||||||
|
```
|
||||||
|
|
||||||
|
### 8.3 `smart_get_function_info`
|
||||||
|
|
||||||
|
调用履历:
|
||||||
|
|
||||||
|
```text
|
||||||
|
smart_get_function_info
|
||||||
|
-> SmartJsonProcessor::getFunctionInfo
|
||||||
|
-> FunctionRegistry::getFunction
|
||||||
|
```
|
||||||
|
|
||||||
|
### 8.4 `smart_test_match`
|
||||||
|
|
||||||
|
调用履历:
|
||||||
|
|
||||||
|
```text
|
||||||
|
smart_test_match
|
||||||
|
-> 解析 req_cmd
|
||||||
|
-> FunctionRegistry::getFunction
|
||||||
|
-> SmartJsonProcessor::parseJsonParams
|
||||||
|
-> FunctionRegistry::smartMatchParams
|
||||||
|
-> 返回匹配结果,不真正执行业务函数
|
||||||
|
```
|
||||||
|
|
||||||
|
## 9. 文件职责一览
|
||||||
|
|
||||||
|
### 9.1 入口层
|
||||||
|
|
||||||
|
- `src/main.cpp`
|
||||||
|
- 构建入口,基本不承接业务
|
||||||
|
- `src/smart_json_wrapper.cpp`
|
||||||
|
- 所有 WASM 导出函数
|
||||||
|
- 业务接口入口和智能数学接口入口都在这里
|
||||||
|
|
||||||
|
### 9.2 业务分发层
|
||||||
|
|
||||||
|
- `src/api/KinematicsWebAPI.Core.cpp`
|
||||||
|
- 总分发
|
||||||
|
- `src/api/KinematicsWebAPI.RobotCommands.cpp`
|
||||||
|
- 机器人接口分支
|
||||||
|
- `src/api/KinematicsWebAPI.SpcCommands.cpp`
|
||||||
|
- SPC 接口分支
|
||||||
|
- `src/api/KinematicsWebAPI.FourBarCommands.cpp`
|
||||||
|
- 四连杆接口分支
|
||||||
|
- `src/api/KinematicsWebAPI.QuadrupedCommands.cpp`
|
||||||
|
- 四足接口分支
|
||||||
|
|
||||||
|
### 9.3 业务实现层
|
||||||
|
|
||||||
|
- `src/RobotManager.cpp`
|
||||||
|
- 机器人实例生命周期管理
|
||||||
|
- `src/Robot.cpp`
|
||||||
|
- 机器人 KDL 正解、逆解、轨迹插补、前端返回格式组装
|
||||||
|
- `src/spc_core.cpp`
|
||||||
|
- SPC 计算主体
|
||||||
|
- `src/FourBarMechanism/*.cpp`
|
||||||
|
- 连杆/曲柄滑块算法
|
||||||
|
- `src/QuadrupedRobotSimulation/*.cpp`
|
||||||
|
- 四足机器人轨迹与反解
|
||||||
|
|
||||||
|
## 10. 如果你要继续顺着看,推荐顺序
|
||||||
|
|
||||||
|
如果你想先搞清“机器人接口”:
|
||||||
|
|
||||||
|
1. `src/smart_json_wrapper.cpp` 里的 `func`
|
||||||
|
2. `src/api/KinematicsWebAPI.Core.cpp`
|
||||||
|
3. `src/api/KinematicsWebAPI.RobotCommands.cpp`
|
||||||
|
4. `src/RobotManager.cpp`
|
||||||
|
5. `src/Robot.cpp`
|
||||||
|
|
||||||
|
如果你想先搞清“四足机器人接口”:
|
||||||
|
|
||||||
|
1. `src/api/KinematicsWebAPI.QuadrupedCommands.cpp`
|
||||||
|
2. `src/QuadrupedRobotSimulation/KinematicsHelper.cpp`
|
||||||
|
3. `src/QuadrupedRobotSimulation/KinematicsSimulation.cpp`
|
||||||
|
4. `src/QuadrupedRobotSimulation/KinematicsReverse.cpp`
|
||||||
|
5. `src/QuadrupedRobotSimulation/CompleteJsonExporter.cpp`
|
||||||
|
|
||||||
|
如果你想先搞清“SPC 接口”:
|
||||||
|
|
||||||
|
1. `src/api/KinematicsWebAPI.SpcCommands.cpp`
|
||||||
|
2. `src/spc_core.cpp`
|
||||||
|
3. `include/spc_core.h`
|
||||||
|
|
||||||
@@ -19,6 +19,24 @@ private:
|
|||||||
|
|
||||||
std::function<void(const std::string &)> onLog;
|
std::function<void(const std::string &)> onLog;
|
||||||
|
|
||||||
|
// 按功能域分发请求
|
||||||
|
json dispatchCommand(const std::string &req_cmd, const json &req_param);
|
||||||
|
|
||||||
|
// 机器人运动学与机器人管理接口
|
||||||
|
json handleRobotCommand(const std::string &req_cmd, const json &req_param);
|
||||||
|
|
||||||
|
// SPC 接口
|
||||||
|
json handleSpcCommand(const std::string &req_cmd, const json &req_param);
|
||||||
|
|
||||||
|
// 连杆机构接口
|
||||||
|
json handleFourBarCommand(const std::string &req_cmd, const json &req_param);
|
||||||
|
|
||||||
|
// 四足机器人接口
|
||||||
|
json handleQuadrupedCommand(const std::string &req_cmd, const json &req_param);
|
||||||
|
|
||||||
|
// 未知命令的默认响应
|
||||||
|
json createUnknownCommandResponse(const std::string &req_cmd, const json &req_param) const;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
KinematicsWebAPI();
|
KinematicsWebAPI();
|
||||||
|
|
||||||
|
|||||||
237
include/Robot.h
237
include/Robot.h
@@ -1,17 +1,20 @@
|
|||||||
#ifndef ROBOT_H
|
#ifndef ROBOT_H
|
||||||
#define ROBOT_H
|
#define ROBOT_H
|
||||||
#include <unordered_map>
|
|
||||||
#include <kdl/chain.hpp>
|
#include <kdl/chain.hpp>
|
||||||
#include <kdl/chainfksolverpos_recursive.hpp>
|
#include <kdl/chainfksolverpos_recursive.hpp>
|
||||||
#include <kdl/chainiksolvervel_pinv.hpp>
|
#include <kdl/chainiksolverpos_lma.hpp>
|
||||||
#include <kdl/chainiksolverpos_nr.hpp>
|
#include <kdl/chainiksolverpos_nr.hpp>
|
||||||
#include <kdl/chainiksolverpos_lma.hpp> // 锟斤拷锟斤拷LMA锟斤拷锟斤拷锟酵凤拷募锟<E58B9F>
|
#include <kdl/chainiksolvervel_pinv.hpp>
|
||||||
#include <kdl/frames.hpp>
|
#include <kdl/frames.hpp>
|
||||||
#include <kdl/jntarray.hpp>
|
#include <kdl/jntarray.hpp>
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
// 锟斤拷 Robot.h 锟斤拷锟斤拷锟斤拷
|
#include <unordered_map>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
|
||||||
class Robot
|
class Robot
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
@@ -19,225 +22,235 @@ private:
|
|||||||
KDL::ChainFkSolverPos_recursive *fkSolver;
|
KDL::ChainFkSolverPos_recursive *fkSolver;
|
||||||
KDL::ChainIkSolverVel_pinv *ikVelSolver;
|
KDL::ChainIkSolverVel_pinv *ikVelSolver;
|
||||||
KDL::ChainIkSolverPos_NR *ikSolverNR;
|
KDL::ChainIkSolverPos_NR *ikSolverNR;
|
||||||
KDL::ChainIkSolverPos_LMA *ikSolverLMA; // 锟斤拷锟斤拷LMA锟斤拷锟斤拷锟<E68BB7>
|
KDL::ChainIkSolverPos_LMA *ikSolverLMA;
|
||||||
bool m_initialized;
|
bool m_initialized;
|
||||||
|
|
||||||
std::unordered_map<std::string, std::string> jointChildLinkUuidMap; // joint锟斤拷锟狡碉拷child link UUID锟斤拷映锟斤拷
|
// 记录 joint 名称到 child link UUID 的映射,便于前端回写对象姿态。
|
||||||
|
std::unordered_map<std::string, std::string> jointChildLinkUuidMap;
|
||||||
|
|
||||||
// 私锟叫革拷锟斤拷锟斤拷锟斤拷
|
// 内部工具方法。
|
||||||
double quaternionAngleDifference(const double q1[4], const double q2[4]);
|
double quaternionAngleDifference(const double q1[4], const double q2[4]);
|
||||||
int calculateAutoSteps(const double startPose[7], const double endPose[7],
|
int calculateAutoSteps(const double startPose[7], const double endPose[7],
|
||||||
int minSteps, int maxSteps,
|
int minSteps, int maxSteps,
|
||||||
double positionResolution, double orientationResolution);
|
double positionResolution, double orientationResolution);
|
||||||
|
|
||||||
// 锟斤拷锟斤拷私锟叫凤拷锟斤拷
|
|
||||||
bool parseJointChildLinkUuidsFromUrdf(const std::string &urdfString);
|
bool parseJointChildLinkUuidsFromUrdf(const std::string &urdfString);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
* @brief 默锟较癸拷锟届函锟斤拷
|
* @brief 默认构造函数。
|
||||||
*/
|
*/
|
||||||
Robot();
|
Robot();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 锟斤拷URDF锟街凤拷锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷锟<EFBFBD>
|
* @brief 使用 URDF 字符串直接初始化机器人。
|
||||||
* @param urdfString URDF锟斤拷式锟斤拷锟街凤拷锟斤拷
|
* @param urdfString URDF 格式字符串。
|
||||||
*/
|
*/
|
||||||
explicit Robot(const std::string &urdfString);
|
explicit Robot(const std::string &urdfString);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷源
|
* @brief 释放运动学求解器资源。
|
||||||
*/
|
*/
|
||||||
~Robot();
|
~Robot();
|
||||||
|
|
||||||
int numberOfJoints;
|
int numberOfJoints;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 锟斤拷URDF锟街凤拷锟斤拷锟斤拷始锟斤拷锟斤拷锟斤拷锟斤拷
|
* @brief 根据 URDF 字符串初始化机器人运动学链。
|
||||||
* @param urdfString URDF锟斤拷式锟斤拷锟街凤拷锟斤拷
|
* @param urdfString URDF 格式字符串。
|
||||||
* @return 锟缴癸拷锟斤拷锟斤拷true锟斤拷失锟杰凤拷锟斤拷false
|
* @return 初始化成功返回 true,否则返回 false。
|
||||||
*/
|
*/
|
||||||
bool initRobot(const std::string &urdfString);
|
bool initRobot(const std::string &urdfString);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷呕锟饺<EFBFBD>joint锟斤拷应锟斤拷child link UUID
|
* @brief 根据关节序号获取 child link UUID。
|
||||||
* @param jointIndex joint锟斤拷锟<EFBFBD> (1, 2, 3, ...)
|
* @param jointIndex 关节序号,从 1 开始。
|
||||||
* @return 锟斤拷应锟斤拷UUID锟街凤拷锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷蚍祷乜锟斤拷址锟斤拷锟<EFBFBD>
|
* @return 找到时返回对应 UUID,未找到返回空字符串。
|
||||||
*/
|
*/
|
||||||
std::string getJointUuidByIndex(int jointIndex) const;
|
std::string getJointUuidByIndex(int jointIndex) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷呕锟饺<EFBFBD>joint锟斤拷锟斤拷
|
* @brief 根据关节序号生成标准关节名称。
|
||||||
* @param jointIndex joint锟斤拷锟<EFBFBD> (1, 2, 3, ...)
|
* @param jointIndex 关节序号,从 1 开始。
|
||||||
* @return joint锟斤拷锟斤拷锟街凤拷锟斤拷锟斤拷锟斤拷 "joint_1", "joint_2" 锟斤拷
|
* @return 如 `joint_1`、`joint_2` 这样的关节名称。
|
||||||
*/
|
*/
|
||||||
static std::string getJointNameByIndex(int jointIndex);
|
static std::string getJointNameByIndex(int jointIndex);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 锟斤拷取锟斤拷锟叫匡拷锟斤拷joint锟斤拷锟斤拷斜锟<EFBFBD>
|
* @brief 获取当前已解析到的全部关节序号列表。
|
||||||
* @return 锟斤拷锟斤拷锟斤拷锟叫匡拷锟斤拷joint锟斤拷诺锟斤拷锟斤拷锟<EFBFBD>
|
* @return 已排序且去重后的关节序号集合。
|
||||||
*/
|
*/
|
||||||
std::vector<int> getAvailableJointIndices() const;
|
std::vector<int> getAvailableJointIndices() const;
|
||||||
|
|
||||||
// 锟斤拷Robot.h锟斤拷锟斤拷锟斤拷
|
/**
|
||||||
|
* @brief 兼容多种命名格式查找关节 UUID。
|
||||||
|
* @param jointIndex 关节序号,从 1 开始。
|
||||||
|
* @return 找到时返回 UUID,未找到返回空字符串。
|
||||||
|
*/
|
||||||
std::string findJointUuidByAnyFormat(int jointIndex) const;
|
std::string findJointUuidByAnyFormat(int jointIndex) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 对姿态序列做逆解,自动进行轨迹插补。
|
||||||
|
*/
|
||||||
json inversePoseStr(const std::string &pose_str, const std::string &q_init_str);
|
json inversePoseStr(const std::string &pose_str, const std::string &q_init_str);
|
||||||
// 锟斤拷锟斤拷锟斤拷锟斤拷筒锟街碉拷锟斤拷锟斤拷锟斤拷锟斤拷锟截诧拷趾锟斤拷锟斤拷锟<E68BB7>
|
|
||||||
|
/**
|
||||||
|
* @brief 对两点姿态按指定步数插补后做逆解。
|
||||||
|
*/
|
||||||
json inversePoseStr2PSteps(const std::string &pose_str, const std::string &q_init_str, const std::int32_t steps);
|
json inversePoseStr2PSteps(const std::string &pose_str, const std::string &q_init_str, const std::int32_t steps);
|
||||||
// 锟斤拷锟斤拷锟斤拷牡锟街<E9949F>锟戒不锟斤拷锟叫诧拷趾锟街憋拷臃锟斤拷囟锟接︼拷锟斤拷锟斤拷锟<E68BB7>
|
|
||||||
|
/**
|
||||||
|
* @brief 直接对输入姿态点做逆解,不做相邻点插补。
|
||||||
|
*/
|
||||||
json inversePoseStrNoDifference(const std::string &pose_str, const std::string &q_init_str);
|
json inversePoseStrNoDifference(const std::string &pose_str, const std::string &q_init_str);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 解析姿态字符串,格式为 `x,y,z,qx,qy,qz,qw;...`。
|
||||||
|
*/
|
||||||
std::vector<std::vector<double>> parsePoseString(const std::string &pose_str);
|
std::vector<std::vector<double>> parsePoseString(const std::string &pose_str);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 解析关节列表字符串,格式为 `j1,j2,j3,j4,j5,j6;...`。
|
||||||
|
*/
|
||||||
std::vector<std::vector<double>> parseJointListString(const std::string &pose_str);
|
std::vector<std::vector<double>> parseJointListString(const std::string &pose_str);
|
||||||
|
|
||||||
std::unordered_map<std::string, std::string> getJointChildLinkUuidMap() const { return jointChildLinkUuidMap; }
|
std::unordered_map<std::string, std::string> getJointChildLinkUuidMap() const { return jointChildLinkUuidMap; }
|
||||||
std::string getJointChildLinkUuid(const std::string &jointName) const;
|
std::string getJointChildLinkUuid(const std::string &jointName) const;
|
||||||
std::vector<double> parseJointString(const std::string &joint_str);
|
std::vector<double> parseJointString(const std::string &joint_str);
|
||||||
|
|
||||||
json kinematicsForwardAllJointsList(std::vector<double> joints);
|
|
||||||
json handleKinematicsForwardAllJoints(const std::string &joints_str);
|
|
||||||
json handleKinematicsForwardAllJoints_objStates(const std::string &joints_str);
|
|
||||||
/**
|
/**
|
||||||
* @brief 锟斤拷锟斤拷锟斤拷锟斤拷锟角凤拷锟窖筹拷始锟斤拷
|
* @brief 计算所有关节位姿,并按前端需要的 objStates 格式组织数据。
|
||||||
* @return 锟窖筹拷始锟斤拷锟斤拷锟斤拷true
|
*/
|
||||||
|
json kinematicsForwardAllJointsList(std::vector<double> joints);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 批量计算多帧关节正解,输出 OPERATION 结构。
|
||||||
|
*/
|
||||||
|
json handleKinematicsForwardAllJoints(const std::string &joints_str);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 兼容旧格式的全部关节正解输出。
|
||||||
|
*/
|
||||||
|
json handleKinematicsForwardAllJoints_objStates(const std::string &joints_str);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 判断机器人是否已初始化。
|
||||||
|
* @return 已初始化返回 true。
|
||||||
*/
|
*/
|
||||||
bool isInitialized() const { return m_initialized; }
|
bool isInitialized() const { return m_initialized; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 锟斤拷锟斤拷锟剿讹拷学锟斤拷锟<EFBFBD> (NR锟斤拷锟斤拷)
|
* @brief 使用 NR 求解器计算逆运动学。
|
||||||
* @param pose 目锟斤拷位锟剿o拷锟斤拷锟斤拷锟斤拷式[x, y, z, qx, qy, qz, qw]
|
* @param pose 目标位姿,格式为 `[x, y, z, qx, qy, qz, qw]`。
|
||||||
* @param iniJ 锟斤拷始锟截节角度o拷锟斤拷锟斤拷锟斤拷式[6锟斤拷锟截节角讹拷]
|
* @param iniJ 初始关节角,格式为 `[j1, ..., j6]`。
|
||||||
* @param resultJoints 锟斤拷锟斤拷锟斤拷锟截节角度o拷锟斤拷锟斤拷锟斤拷式[6锟斤拷锟截节角讹拷]
|
* @param resultJoints 输出关节角结果。
|
||||||
* @return 锟缴癸拷锟斤拷锟斤拷true锟斤拷失锟杰凤拷锟斤拷false
|
* @return 求解成功返回 true,否则返回 false。
|
||||||
*/
|
*/
|
||||||
bool calculateIK_NR(const double pose[7], const double iniJ[6], double resultJoints[6]);
|
bool calculateIK_NR(const double pose[7], const double iniJ[6], double resultJoints[6]);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 锟斤拷锟斤拷锟剿讹拷学锟斤拷锟<EFBFBD> (LMA锟斤拷锟斤拷)
|
* @brief 使用 LMA 求解器计算逆运动学。
|
||||||
* @param pose 目锟斤拷位锟剿o拷锟斤拷锟斤拷锟斤拷式[x, y, z, qx, qy, qz, qw]
|
* @param pose 目标位姿,格式为 `[x, y, z, qx, qy, qz, qw]`。
|
||||||
* @param iniJ 锟斤拷始锟截节角度o拷锟斤拷锟斤拷锟斤拷式[6锟斤拷锟截节角讹拷]
|
* @param iniJ 初始关节角,格式为 `[j1, ..., j6]`。
|
||||||
* @param resultJoints 锟斤拷锟斤拷锟斤拷锟截节角度o拷锟斤拷锟斤拷锟斤拷式[6锟斤拷锟截节角讹拷]
|
* @param resultJoints 输出关节角结果。
|
||||||
* @param eps 锟斤拷锟斤拷锟斤拷锟斤拷 (默锟斤拷: 1e-8)
|
* @param eps 迭代收敛精度。
|
||||||
* @param maxiter 锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷 (默锟斤拷: 3000)
|
* @param maxiter 最大迭代次数。
|
||||||
* @param eps_joints 锟截斤拷锟捷诧拷 (默锟斤拷: 1e-12)
|
* @param eps_joints 关节收敛阈值。
|
||||||
* @return 锟缴癸拷锟斤拷锟斤拷true锟斤拷失锟杰凤拷锟斤拷false
|
* @return 求解成功返回 true,否则返回 false。
|
||||||
*/
|
*/
|
||||||
bool calculateIK_LMA(const double pose[7], const double iniJ[6], double resultJoints[6],
|
bool calculateIK_LMA(const double pose[7], const double iniJ[6], double resultJoints[6],
|
||||||
double eps = 1e-8, int maxiter = 3000, double eps_joints = 1e-12);
|
double eps = 1e-8, int maxiter = 3000, double eps_joints = 1e-12);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 锟斤拷锟斤拷锟剿讹拷学锟斤拷锟<EFBFBD> - TCP位锟斤拷
|
* @brief 计算 TCP 的正运动学结果。
|
||||||
* @param joints 锟斤拷锟斤拷亟诮嵌龋锟斤拷锟斤拷锟斤拷锟绞絒6锟斤拷锟截节角讹拷]
|
* @param joints 输入关节角,格式为 `[j1, ..., j6]`。
|
||||||
* @param tcpPose 锟斤拷锟絋CP位锟剿o拷锟斤拷锟斤拷锟斤拷式[x, y, z, qx, qy, qz, qw]
|
* @param tcpPose 输出 TCP 位姿,格式为 `[x, y, z, qx, qy, qz, qw]`。
|
||||||
* @return 锟缴癸拷锟斤拷锟斤拷true锟斤拷失锟杰凤拷锟斤拷false
|
* @return 计算成功返回 true,否则返回 false。
|
||||||
*/
|
*/
|
||||||
bool calculateFK_TCP(const double joints[6], double tcpPose[7]);
|
bool calculateFK_TCP(const double joints[6], double tcpPose[7]);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 锟斤拷锟斤拷锟剿讹拷学锟斤拷锟<EFBFBD> - 锟斤拷锟叫关斤拷位锟斤拷
|
* @brief 计算每个关节节点的正运动学结果。
|
||||||
* @param joints 锟斤拷锟斤拷亟诮嵌龋锟斤拷锟斤拷锟斤拷锟绞絒6锟斤拷锟截节角讹拷]
|
* @param joints 输入关节角,格式为 `[j1, ..., j6]`。
|
||||||
* @param jointPoses 锟斤拷锟斤拷锟斤拷泄亟锟轿伙拷耍锟斤拷锟斤拷锟斤拷锟绞絒6*7锟斤拷元锟斤拷]
|
* @param jointPoses 输出全部关节位姿,共 6 组,每组 7 个值。
|
||||||
* @return 锟缴癸拷锟斤拷锟斤拷true锟斤拷失锟杰凤拷锟斤拷false
|
* @return 计算成功返回 true,否则返回 false。
|
||||||
*/
|
*/
|
||||||
bool calculateFK_AllJointsforwardKinematics(const double joints[6], double jointPoses[42]);
|
bool calculateFK_AllJointsforwardKinematics(const double joints[6], double jointPoses[42]);
|
||||||
bool forwardKinematics(const double joints[6], double jointPoses[42]);
|
bool forwardKinematics(const double joints[6], double jointPoses[42]);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 锟斤拷取锟斤拷锟斤拷锟斤拷锟剿讹拷锟斤拷
|
* @brief 获取当前 KDL 运动学链。
|
||||||
* @return KDL锟剿讹拷锟斤拷锟斤拷锟斤拷
|
|
||||||
*/
|
*/
|
||||||
const KDL::Chain &getKinematicChain() const { return kinematicChain; }
|
const KDL::Chain &getKinematicChain() const { return kinematicChain; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 锟斤拷取锟截斤拷锟斤拷锟斤拷
|
* @brief 获取机器人关节数量。
|
||||||
* @return 锟截斤拷锟斤拷锟斤拷
|
|
||||||
*/
|
*/
|
||||||
int getNumberOfJoints() const { return kinematicChain.getNrOfJoints(); }
|
int getNumberOfJoints() const { return kinematicChain.getNrOfJoints(); }
|
||||||
|
|
||||||
// 锟届迹锟芥划锟斤拷锟斤拷
|
|
||||||
/**
|
/**
|
||||||
* @brief 锟斤拷锟斤拷锟斤拷态锟斤拷墓旒o拷婊<EFBFBD>锟斤拷锟斤拷锟斤拷姹撅拷锟<EFBFBD>
|
* @brief 生成姿态轨迹,输出为固定数组。
|
||||||
* @param pose1 锟斤拷始位锟斤拷 [x, y, z, qx, qy, qz, qw]
|
* @param pose1 起点位姿。
|
||||||
* @param pose2 锟斤拷止位锟斤拷 [x, y, z, qx, qy, qz, qw]
|
* @param pose2 终点位姿。
|
||||||
* @param outputPoses 锟斤拷锟斤拷旒o拷锟斤拷锟斤拷锟<EFBFBD>
|
* @param outputPoses 输出轨迹数组。
|
||||||
* @param npoint 锟届迹锟斤拷锟斤拷锟斤拷锟斤拷0锟斤拷示锟皆讹拷锟斤拷锟姐)
|
* @param npoint 轨迹点数量,传 0 表示自动计算。
|
||||||
* @return 实锟斤拷锟斤拷锟缴的轨迹锟斤拷锟斤拷锟斤拷锟斤拷失锟杰凤拷锟斤拷-1
|
* @return 实际生成的轨迹点数量,失败返回 -1。
|
||||||
*/
|
*/
|
||||||
int trajectoryPlanning(const double pose1[7], const double pose2[7],
|
int trajectoryPlanning(const double pose1[7], const double pose2[7],
|
||||||
double outputPoses[][7], int npoint = 0);
|
double outputPoses[][7], int npoint = 0);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 锟斤拷锟斤拷锟斤拷态锟斤拷墓旒o拷婊<EFBFBD>锟斤拷锟斤拷锟斤拷姹撅拷锟斤拷锟斤拷锟斤拷vector锟斤拷
|
* @brief 生成姿态轨迹,输出为二维向量。
|
||||||
* @param pose1 锟斤拷始位锟斤拷 [x, y, z, qx, qy, qz, qw]
|
* @param pose1 起点位姿。
|
||||||
* @param pose2 锟斤拷止位锟斤拷 [x, y, z, qx, qy, qz, qw]
|
* @param pose2 终点位姿。
|
||||||
* @param outputPoses 锟斤拷锟斤拷旒o拷锟絭ector锟斤拷锟斤拷锟斤拷预锟饺凤拷锟斤拷锟节存)
|
* @param outputPoses 输出轨迹点集合。
|
||||||
* @param npoint 锟届迹锟斤拷锟斤拷锟斤拷锟斤拷0锟斤拷示锟皆讹拷锟斤拷锟姐)
|
* @param npoint 轨迹点数量,传 0 表示自动计算。
|
||||||
* @return 实锟斤拷锟斤拷锟缴的轨迹锟斤拷锟斤拷锟斤拷锟斤拷失锟杰凤拷锟斤拷-1
|
* @return 实际生成的轨迹点数量,失败返回 -1。
|
||||||
*/
|
*/
|
||||||
int trajectoryPlanning(const double pose1[7], const double pose2[7],
|
int trajectoryPlanning(const double pose1[7], const double pose2[7],
|
||||||
std::vector<std::vector<double>> &outputPoses,
|
std::vector<std::vector<double>> &outputPoses,
|
||||||
int npoint = 0);
|
int npoint = 0);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 锟斤拷锟斤拷锟斤拷态锟斤拷墓旒o拷婊<EFBFBD>锟斤拷vector锟芥本锟斤拷
|
* @brief 使用 `std::vector<double>` 形式输入位姿生成轨迹。
|
||||||
* @param pose1 锟斤拷始位锟斤拷
|
* @param pose1 起点位姿。
|
||||||
* @param pose2 锟斤拷止位锟斤拷
|
* @param pose2 终点位姿。
|
||||||
* @param outputPoses 锟斤拷锟斤拷旒o拷锟斤拷锟斤拷锟<EFBFBD>
|
* @param outputPoses 输出轨迹点集合。
|
||||||
* @param npoint 锟届迹锟斤拷锟斤拷锟斤拷锟斤拷0锟斤拷示锟皆讹拷锟斤拷锟姐)
|
* @param npoint 轨迹点数量,传 0 表示自动计算。
|
||||||
* @return 实锟斤拷锟斤拷锟缴的轨迹锟斤拷锟斤拷锟斤拷锟斤拷失锟杰凤拷锟斤拷-1
|
* @return 实际生成的轨迹点数量,失败返回 -1。
|
||||||
*/
|
*/
|
||||||
int trajectoryPlanning(const std::vector<double> &pose1, const std::vector<double> &pose2,
|
int trajectoryPlanning(const std::vector<double> &pose1, const std::vector<double> &pose2,
|
||||||
std::vector<std::vector<double>> &outputPoses, int npoint = 0);
|
std::vector<std::vector<double>> &outputPoses, int npoint = 0);
|
||||||
|
|
||||||
// 锟斤拷锟竭猴拷锟斤拷
|
|
||||||
/**
|
|
||||||
* @brief 锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷元锟斤拷之锟斤拷慕嵌炔锟<E78294>
|
|
||||||
* @param q1 锟斤拷元锟斤拷1 [qx, qy, qz, qw]
|
|
||||||
* @param q2 锟斤拷元锟斤拷2 [qx, qy, qz, qw]
|
|
||||||
* @return 锟角度差(锟斤拷锟饺o拷
|
|
||||||
*/
|
|
||||||
// static double quaternionAngleDifference(const double q1[4], const double q2[4]);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 锟皆讹拷锟斤拷锟斤拷旒o拷婊<E68BB7>锟斤拷锟斤拷锟斤拷锟<E68BB7>
|
|
||||||
* @param startPose 锟斤拷始位锟斤拷 [x, y, z, qx, qy, qz, qw]
|
|
||||||
* @param endPose 锟斤拷止位锟斤拷 [x, y, z, qx, qy, qz, qw]
|
|
||||||
* @param minSteps 锟斤拷小锟斤拷锟斤拷
|
|
||||||
* @param maxSteps 锟斤拷锟斤拷锟<E68BB7>
|
|
||||||
* @param positionResolution 位锟矫分憋拷锟绞o拷锟阶o拷
|
|
||||||
* @param orientationResolution 锟斤拷态锟街憋拷锟绞o拷锟斤拷锟饺o拷
|
|
||||||
* @return 锟狡硷拷锟侥轨迹锟斤拷锟斤拷锟斤拷
|
|
||||||
*/
|
|
||||||
// static int calculateAutoSteps(const double startPose[7], const double endPose[7],
|
|
||||||
// int minSteps = 10, int maxSteps = 100,
|
|
||||||
// double positionResolution = 0.01,
|
|
||||||
// double orientationResolution = 0.1);
|
|
||||||
|
|
||||||
// 锟斤拷取锟斤拷锟斤拷锟斤拷锟斤拷息
|
|
||||||
// const KDL::Chain& getKinematicChain() const { return kinematicChain; }
|
|
||||||
// int getNumberOfJoints() const { return kinematicChain.getNrOfJoints(); }
|
|
||||||
|
|
||||||
int calculateAutoSteps(const std::vector<double> &startPose, const std::vector<double> &endPose,
|
int calculateAutoSteps(const std::vector<double> &startPose, const std::vector<double> &endPose,
|
||||||
int minSteps, int maxSteps,
|
int minSteps, int maxSteps,
|
||||||
double positionResolution, double orientationResolution);
|
double positionResolution, double orientationResolution);
|
||||||
double quaternionAngleDifference(const std::vector<double> &q1, const std::vector<double> &q2);
|
double quaternionAngleDifference(const std::vector<double> &q1, const std::vector<double> &q2);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 锟斤拷锟斤拷锟剿讹拷学锟斤拷锟<EFBFBD> (LMA锟斤拷锟斤拷) - 锟斤拷锟斤拷vector锟芥本
|
* @brief 使用 LMA 求解器计算逆解,输入为数组。
|
||||||
* @param pose 目锟斤拷位锟斤拷 [x, y, z, qx, qy, qz, qw]
|
|
||||||
* @param iniJ 锟斤拷始锟截节角讹拷 [6锟斤拷锟截节角讹拷]
|
|
||||||
* @param eps 锟斤拷锟斤拷锟斤拷锟斤拷
|
|
||||||
* @param maxiter 锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷
|
|
||||||
* @param eps_joints 锟截斤拷锟捷诧拷
|
|
||||||
* @return 锟截节角讹拷vector
|
|
||||||
*/
|
*/
|
||||||
std::vector<double> inverse(const double pose[7], const double iniJ[6],
|
std::vector<double> inverse(const double pose[7], const double iniJ[6],
|
||||||
double eps = 1e-8, int maxiter = 3000,
|
double eps = 1e-8, int maxiter = 3000,
|
||||||
double eps_joints = 1e-12);
|
double eps_joints = 1e-12);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 使用 LMA 求解器计算逆解,输入为向量。
|
||||||
|
*/
|
||||||
std::vector<double> inverse(const std::vector<double> &pose,
|
std::vector<double> inverse(const std::vector<double> &pose,
|
||||||
const std::vector<double> &iniJ,
|
const std::vector<double> &iniJ,
|
||||||
double eps = 1e-8, int maxiter = 3000,
|
double eps = 1e-8, int maxiter = 3000,
|
||||||
double eps_joints = 1e-12);
|
double eps_joints = 1e-12);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 使用默认零位初值计算逆解,输入为数组。
|
||||||
|
*/
|
||||||
std::vector<double> inverse(const double pose[7],
|
std::vector<double> inverse(const double pose[7],
|
||||||
double eps = 1e-8, int maxiter = 3000,
|
double eps = 1e-8, int maxiter = 3000,
|
||||||
double eps_joints = 1e-12);
|
double eps_joints = 1e-12);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 使用默认零位初值计算逆解,输入为向量。
|
||||||
|
*/
|
||||||
std::vector<double> inverse(const std::vector<double> &pose,
|
std::vector<double> inverse(const std::vector<double> &pose,
|
||||||
double eps = 1e-8, int maxiter = 3000,
|
double eps = 1e-8, int maxiter = 3000,
|
||||||
double eps_joints = 1e-12);
|
double eps_joints = 1e-12);
|
||||||
|
|||||||
@@ -1,37 +1,33 @@
|
|||||||
#ifndef UTILS_H
|
#ifndef UTILS_H
|
||||||
#define UTILS_H
|
#define UTILS_H
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include "nlohmann/json.hpp"
|
#include <string>
|
||||||
|
|
||||||
// // ǰ<><C7B0><EFBFBD><EFBFBD><EFBFBD><EFBFBD> httplib <20><> Response <20><>
|
#include "nlohmann/json.hpp"
|
||||||
// namespace httplib {
|
|
||||||
// class Response;
|
|
||||||
// }
|
|
||||||
|
|
||||||
using json = nlohmann::json;
|
using json = nlohmann::json;
|
||||||
|
|
||||||
namespace utils
|
namespace utils
|
||||||
{
|
{
|
||||||
// ʱ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>غ<EFBFBD><EFBFBD><EFBFBD>
|
// 时间相关工具。
|
||||||
std::string get_current_time();
|
std::string get_current_time();
|
||||||
std::string get_current_timestamp();
|
std::string get_current_timestamp();
|
||||||
|
|
||||||
// JSON <EFBFBD><EFBFBD>Ӧ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
// 通用 JSON 响应构造。
|
||||||
json create_error_response(int code, const std::string &message, const std::string &details = "");
|
json create_error_response(int code, const std::string &message, const std::string &details = "");
|
||||||
json create_success_response(const json &data = {}, const std::string &message = "Success");
|
json create_success_response(const json &data = {}, const std::string &message = "Success");
|
||||||
|
|
||||||
// UTF-8 <EFBFBD><EFBFBD>֤
|
// UTF-8 校验与清洗。
|
||||||
bool is_valid_utf8(const std::string &str);
|
bool is_valid_utf8(const std::string &str);
|
||||||
std::string sanitize_utf8(const std::string &str);
|
std::string sanitize_utf8(const std::string &str);
|
||||||
|
|
||||||
// API <EFBFBD><EFBFBD>Ӧ<EFBFBD><EFBFBD>ʽ
|
// 业务 API 响应格式。
|
||||||
json create_api_response(bool success, int code, const std::string &msg,
|
json create_api_response(bool success, int code, const std::string &msg,
|
||||||
const std::string &req_code = "", const std::string &req_from = "",
|
const std::string &req_code = "", const std::string &req_from = "",
|
||||||
const std::string &req_cmd = "", const json &res_data = json::object());
|
const std::string &req_cmd = "", const json &res_data = json::object());
|
||||||
|
|
||||||
// Base64 <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>URDF<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
// Base64 与 URDF 处理。
|
||||||
std::string base64_decode(const std::string &encoded_string);
|
std::string base64_decode(const std::string &encoded_string);
|
||||||
std::string base64_to_urdf(const std::string &base64_urdf);
|
std::string base64_to_urdf(const std::string &base64_urdf);
|
||||||
bool save_base64_urdf_to_file(const std::string &base64_urdf, const std::string &filename);
|
bool save_base64_urdf_to_file(const std::string &base64_urdf, const std::string &filename);
|
||||||
@@ -39,35 +35,34 @@ namespace utils
|
|||||||
bool save_urdf_string_to_file(const std::string &urdf_content, const std::string &filename);
|
bool save_urdf_string_to_file(const std::string &urdf_content, const std::string &filename);
|
||||||
|
|
||||||
} // namespace utils
|
} // namespace utils
|
||||||
|
|
||||||
class StringUtils
|
class StringUtils
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
* @brief 将包含一个分隔符的字符串拆分成两部分
|
* @brief 将包含单个分隔符的字符串拆分为两部分。
|
||||||
* @param str 要拆分的字符串
|
* @param str 要拆分的字符串。
|
||||||
* @param delimiter 分隔符,默认为'_'
|
* @param delimiter 分隔符,默认为 `_`。
|
||||||
* @return std::pair<std::string, std::string> 包含两个部分的pair
|
* @return 第一个元素为分隔符前的内容,第二个元素为分隔符后的内容。
|
||||||
* 第一个元素是分隔符前的部分,第二个是分隔符后的部分
|
|
||||||
* 如果未找到分隔符,第二个元素为空字符串
|
|
||||||
*/
|
*/
|
||||||
static std::pair<std::string, std::string> splitByDelimiter(
|
static std::pair<std::string, std::string> splitByDelimiter(
|
||||||
const std::string &str,
|
const std::string &str,
|
||||||
char delimiter = '_');
|
char delimiter = '_');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 专门拆分"A_B"格式的字符串
|
* @brief 专门拆分 `A_B` 形式的字符串。
|
||||||
* @param str 要拆分的字符串
|
* @param str 要拆分的字符串。
|
||||||
* @return std::pair<std::string, std::string> 包含两个部分的pair
|
* @return 拆分后的两段内容。
|
||||||
*/
|
*/
|
||||||
static std::pair<std::string, std::string> splitA_B(const std::string &str);
|
static std::pair<std::string, std::string> splitA_B(const std::string &str);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 将字符串按分隔符拆分成两个部分,支持引用参数返回
|
* @brief 将字符串按分隔符拆成两段,并通过引用返回结果。
|
||||||
* @param str 要拆分的字符串
|
* @param str 要拆分的字符串。
|
||||||
* @param part1 返回第一部分
|
* @param part1 返回第一段。
|
||||||
* @param part2 返回第二部分
|
* @param part2 返回第二段。
|
||||||
* @param delimiter 分隔符,默认为'_'
|
* @param delimiter 分隔符,默认为 `_`。
|
||||||
* @return bool 如果找到分隔符返回true,否则返回false
|
* @return 找到分隔符返回 true,否则返回 false。
|
||||||
*/
|
*/
|
||||||
static bool splitToTwoParts(
|
static bool splitToTwoParts(
|
||||||
const std::string &str,
|
const std::string &str,
|
||||||
@@ -76,14 +71,15 @@ public:
|
|||||||
char delimiter = '_');
|
char delimiter = '_');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 严格拆分,必须包含且只包含一个分隔符
|
* @brief 严格拆分字符串,要求只出现一个分隔符。
|
||||||
* @param str 要拆分的字符串
|
* @param str 要拆分的字符串。
|
||||||
* @param delimiter 分隔符,默认为'_'
|
* @param delimiter 分隔符,默认为 `_`。
|
||||||
* @return std::pair<std::string, std::string> 包含两个部分的pair
|
* @return 拆分后的两段内容。
|
||||||
* @throws std::invalid_argument 如果没有找到分隔符或找到多个分隔符
|
* @throws std::invalid_argument 当分隔符不存在或出现多次时抛出异常。
|
||||||
*/
|
*/
|
||||||
static std::pair<std::string, std::string> splitStrict(
|
static std::pair<std::string, std::string> splitStrict(
|
||||||
const std::string &str,
|
const std::string &str,
|
||||||
char delimiter = '_');
|
char delimiter = '_');
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // UTILS_H
|
#endif // UTILS_H
|
||||||
760
public/index.html
Normal file
760
public/index.html
Normal file
@@ -0,0 +1,760 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="zh-CN">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>smart_wasm Local Debug</title>
|
||||||
|
<style>
|
||||||
|
:root {
|
||||||
|
--bg: #f3efe5;
|
||||||
|
--panel: #fffaf0;
|
||||||
|
--line: #d2c7b3;
|
||||||
|
--text: #1f2a30;
|
||||||
|
--muted: #6e7a80;
|
||||||
|
--accent: #0d6b5f;
|
||||||
|
--accent-2: #d7852f;
|
||||||
|
--error: #b7402a;
|
||||||
|
}
|
||||||
|
|
||||||
|
* {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
font-family: 'Segoe UI', 'Microsoft YaHei', sans-serif;
|
||||||
|
color: var(--text);
|
||||||
|
background:
|
||||||
|
radial-gradient(
|
||||||
|
circle at top left,
|
||||||
|
rgba(215, 133, 47, 0.18),
|
||||||
|
transparent 30%
|
||||||
|
),
|
||||||
|
linear-gradient(135deg, #f7f1e3 0%, #e8efe9 100%);
|
||||||
|
min-height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
.shell {
|
||||||
|
max-width: 1100px;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 32px 20px 48px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero {
|
||||||
|
display: grid;
|
||||||
|
gap: 16px;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero h1 {
|
||||||
|
margin: 0;
|
||||||
|
font-size: clamp(28px, 5vw, 48px);
|
||||||
|
line-height: 1.05;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero p {
|
||||||
|
margin: 0;
|
||||||
|
color: var(--muted);
|
||||||
|
max-width: 720px;
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.layout {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1.05fr 0.95fr;
|
||||||
|
gap: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card {
|
||||||
|
background: rgba(255, 250, 240, 0.9);
|
||||||
|
backdrop-filter: blur(10px);
|
||||||
|
border: 1px solid var(--line);
|
||||||
|
border-radius: 20px;
|
||||||
|
box-shadow: 0 20px 40px rgba(38, 50, 56, 0.08);
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-head {
|
||||||
|
padding: 18px 20px;
|
||||||
|
border-bottom: 1px solid var(--line);
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-head h2 {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status {
|
||||||
|
font-size: 13px;
|
||||||
|
color: var(--muted);
|
||||||
|
padding: 6px 10px;
|
||||||
|
border: 1px solid var(--line);
|
||||||
|
border-radius: 999px;
|
||||||
|
background: rgba(255, 255, 255, 0.7);
|
||||||
|
}
|
||||||
|
|
||||||
|
.body {
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
label {
|
||||||
|
display: block;
|
||||||
|
font-size: 13px;
|
||||||
|
color: var(--muted);
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
textarea {
|
||||||
|
width: 100%;
|
||||||
|
min-height: 320px;
|
||||||
|
resize: vertical;
|
||||||
|
border: 1px solid var(--line);
|
||||||
|
border-radius: 14px;
|
||||||
|
padding: 14px;
|
||||||
|
font:
|
||||||
|
13px/1.5 Consolas,
|
||||||
|
'Courier New',
|
||||||
|
monospace;
|
||||||
|
color: var(--text);
|
||||||
|
background: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.actions {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 12px;
|
||||||
|
margin-top: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sample-bar {
|
||||||
|
display: grid;
|
||||||
|
gap: 10px;
|
||||||
|
margin-top: 16px;
|
||||||
|
padding: 14px;
|
||||||
|
border: 1px solid var(--line);
|
||||||
|
border-radius: 14px;
|
||||||
|
background: rgba(255, 255, 255, 0.72);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sample-row {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 10px;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
width: 100%;
|
||||||
|
border: 1px solid var(--line);
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 11px 12px;
|
||||||
|
font-size: 14px;
|
||||||
|
color: var(--text);
|
||||||
|
background: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
border: 0;
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 12px 16px;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 600;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.primary {
|
||||||
|
background: var(--accent);
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.secondary {
|
||||||
|
background: #fff;
|
||||||
|
color: var(--text);
|
||||||
|
border: 1px solid var(--line);
|
||||||
|
}
|
||||||
|
|
||||||
|
pre {
|
||||||
|
margin: 0;
|
||||||
|
min-height: 420px;
|
||||||
|
padding: 16px;
|
||||||
|
overflow: auto;
|
||||||
|
background: #172126;
|
||||||
|
color: #e8f2ef;
|
||||||
|
font:
|
||||||
|
13px/1.5 Consolas,
|
||||||
|
'Courier New',
|
||||||
|
monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
.response-shell {
|
||||||
|
display: grid;
|
||||||
|
gap: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.response-meta {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||||
|
gap: 10px;
|
||||||
|
padding: 16px 20px;
|
||||||
|
background: rgba(17, 30, 35, 0.04);
|
||||||
|
border-bottom: 1px solid var(--line);
|
||||||
|
}
|
||||||
|
|
||||||
|
.response-meta-item {
|
||||||
|
padding: 10px 12px;
|
||||||
|
border: 1px solid rgba(210, 199, 179, 0.8);
|
||||||
|
border-radius: 12px;
|
||||||
|
background: rgba(255, 255, 255, 0.75);
|
||||||
|
}
|
||||||
|
|
||||||
|
.response-meta-label {
|
||||||
|
font-size: 12px;
|
||||||
|
color: var(--muted);
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.response-meta-value {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--text);
|
||||||
|
word-break: break-all;
|
||||||
|
}
|
||||||
|
|
||||||
|
.response-meta-value.is-ok {
|
||||||
|
color: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.response-meta-value.is-error {
|
||||||
|
color: var(--error);
|
||||||
|
}
|
||||||
|
|
||||||
|
.json-key {
|
||||||
|
color: #7dd3fc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.json-string {
|
||||||
|
color: #f9d65c;
|
||||||
|
}
|
||||||
|
|
||||||
|
.json-number {
|
||||||
|
color: #f59e0b;
|
||||||
|
}
|
||||||
|
|
||||||
|
.json-boolean {
|
||||||
|
color: #fb7185;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.json-null {
|
||||||
|
color: #c084fc;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tips {
|
||||||
|
display: grid;
|
||||||
|
gap: 10px;
|
||||||
|
margin-top: 18px;
|
||||||
|
color: var(--muted);
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tips code {
|
||||||
|
color: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 900px) {
|
||||||
|
.layout {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
textarea,
|
||||||
|
pre {
|
||||||
|
min-height: 260px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.response-meta {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div style="width: 200px; height: 300px">
|
||||||
|
<iframe
|
||||||
|
id="ModelLibraryPanel_iframe"
|
||||||
|
title="模型库"
|
||||||
|
src="https://www.claudedt.com/dmtModelLibrary"
|
||||||
|
style="width: 100%; height: 100%; border: 0; display: block"
|
||||||
|
></iframe>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="shell">
|
||||||
|
<section class="hero">
|
||||||
|
<h1>smart_wasm 本地调试页</h1>
|
||||||
|
<p>
|
||||||
|
这个页面直接加载
|
||||||
|
<code>public/wasm/smart_math.js</code>,可以在本地验证 WASM
|
||||||
|
初始化、命令调用和返回 JSON。
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="layout">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-head">
|
||||||
|
<h2>请求</h2>
|
||||||
|
<div class="status" id="module-status">WASM 未初始化</div>
|
||||||
|
</div>
|
||||||
|
<div class="body">
|
||||||
|
<label for="request">JSON Request</label>
|
||||||
|
<textarea id="request">
|
||||||
|
{
|
||||||
|
"msg": "list robots after init",
|
||||||
|
"req_code": "CASE_LIST_001",
|
||||||
|
"req_from": "local_debug",
|
||||||
|
"req_cmd": "Cmd_ListRobots",
|
||||||
|
"req_param": {}
|
||||||
|
}</textarea
|
||||||
|
>
|
||||||
|
|
||||||
|
<div class="sample-bar">
|
||||||
|
<label for="sample-select">接口测试样例</label>
|
||||||
|
<div class="sample-row">
|
||||||
|
<select id="sample-select">
|
||||||
|
<option value="listRobots">机器人列表 Cmd_ListRobots</option>
|
||||||
|
<option value="kinematicsForwardZero">
|
||||||
|
运动学正解 Cmd_Kinematics_forward_pose_str
|
||||||
|
</option>
|
||||||
|
<option value="kinematicsForwardAllJoints">
|
||||||
|
全关节正解 Cmd_Kinematics_forward_all_joints
|
||||||
|
</option>
|
||||||
|
<option value="spcBasic">SPC 计算 Cmd_Spc</option>
|
||||||
|
<option value="fourBarValid">
|
||||||
|
四连杆有效样例 Cmd_FourBar_CrankSlider
|
||||||
|
</option>
|
||||||
|
<option value="fourBarInvalid">
|
||||||
|
四连杆异常样例 Cmd_FourBar_CrankSlider
|
||||||
|
</option>
|
||||||
|
<option value="quadrupedPoints">
|
||||||
|
四足姿态点位
|
||||||
|
Cmd_QuadrupedRobot_CalculateAllPointsFromMotorAngles
|
||||||
|
</option>
|
||||||
|
<option value="quadrupedForwardGait">
|
||||||
|
四足步态正解 Cmd_QuadrupedRobot_PerformForwardKinematics
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="sample-row">
|
||||||
|
<button class="secondary" id="sample-btn" type="button">
|
||||||
|
填充选中样例
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="actions">
|
||||||
|
<button class="primary" id="init-btn" type="button">
|
||||||
|
初始化 WASM
|
||||||
|
</button>
|
||||||
|
<button class="primary" id="run-btn" type="button">
|
||||||
|
发送请求
|
||||||
|
</button>
|
||||||
|
<button class="secondary" id="clear-btn" type="button">
|
||||||
|
清空输出
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="tips">
|
||||||
|
<div>
|
||||||
|
1. 先点 <code>初始化 WASM</code>,会调用
|
||||||
|
<code>_init_func()</code>。
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
2. 再点 <code>发送请求</code>,会调用 <code>_func()</code>。
|
||||||
|
</div>
|
||||||
|
<div>3. 默认地址使用 <code>http://localhost:8080/</code>。</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-head">
|
||||||
|
<h2>响应</h2>
|
||||||
|
<div class="status" id="request-status">等待操作</div>
|
||||||
|
</div>
|
||||||
|
<div class="response-shell">
|
||||||
|
<div class="response-meta" id="response-meta">
|
||||||
|
<div class="response-meta-item">
|
||||||
|
<div class="response-meta-label">状态</div>
|
||||||
|
<div class="response-meta-value" id="meta-success">未执行</div>
|
||||||
|
</div>
|
||||||
|
<div class="response-meta-item">
|
||||||
|
<div class="response-meta-label">代码</div>
|
||||||
|
<div class="response-meta-value" id="meta-code">-</div>
|
||||||
|
</div>
|
||||||
|
<div class="response-meta-item">
|
||||||
|
<div class="response-meta-label">命令</div>
|
||||||
|
<div class="response-meta-value" id="meta-cmd">-</div>
|
||||||
|
</div>
|
||||||
|
<div class="response-meta-item">
|
||||||
|
<div class="response-meta-label">请求号</div>
|
||||||
|
<div class="response-meta-value" id="meta-req">-</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<pre id="output">No output yet.</pre>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="./wasm/smart_math.js"></script>
|
||||||
|
<script>
|
||||||
|
let wasmModule = null;
|
||||||
|
|
||||||
|
const moduleStatus = document.getElementById('module-status');
|
||||||
|
const requestStatus = document.getElementById('request-status');
|
||||||
|
const requestBox = document.getElementById('request');
|
||||||
|
const output = document.getElementById('output');
|
||||||
|
const sampleSelect = document.getElementById('sample-select');
|
||||||
|
const metaSuccess = document.getElementById('meta-success');
|
||||||
|
const metaCode = document.getElementById('meta-code');
|
||||||
|
const metaCmd = document.getElementById('meta-cmd');
|
||||||
|
const metaReq = document.getElementById('meta-req');
|
||||||
|
|
||||||
|
const requestSamples = {
|
||||||
|
listRobots: {
|
||||||
|
msg: 'list robots after init',
|
||||||
|
req_code: 'CASE_LIST_001',
|
||||||
|
req_from: 'local_debug',
|
||||||
|
req_cmd: 'Cmd_ListRobots',
|
||||||
|
req_param: {
|
||||||
|
detail: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
kinematicsForwardZero: {
|
||||||
|
msg: 'fk zero pose',
|
||||||
|
req_code: 'CASE_FK_001',
|
||||||
|
req_from: 'local_debug',
|
||||||
|
req_cmd: 'Cmd_Kinematics_forward_pose_str',
|
||||||
|
req_param: {
|
||||||
|
robot_uuid: 'abb_irb120_3_58',
|
||||||
|
q_init_str: '0,0,0,0,0,0',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
kinematicsForwardAllJoints: {
|
||||||
|
msg: 'fk all joints path',
|
||||||
|
req_code: 'CASE_FK_ALL_001',
|
||||||
|
req_from: 'local_debug',
|
||||||
|
req_cmd: 'Cmd_Kinematics_forward_all_joints',
|
||||||
|
req_param: {
|
||||||
|
robot_uuid: 'abb_irb120_3_58',
|
||||||
|
joints_str: '0,0,0,0,0,0;10,20,30,40,50,60',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
spcBasic: {
|
||||||
|
msg: 'spc basic 5x30',
|
||||||
|
req_code: 'CASE_SPC_001',
|
||||||
|
req_from: 'local_debug',
|
||||||
|
req_cmd: 'Cmd_Spc',
|
||||||
|
req_param: {
|
||||||
|
n: 5,
|
||||||
|
k: 30,
|
||||||
|
usl: 1.7,
|
||||||
|
lsl: 1.5,
|
||||||
|
x: [
|
||||||
|
1.55, 1.58, 1.61, 1.6, 1.6, 1.58, 1.63, 1.63, 1.62, 1.63, 1.62,
|
||||||
|
1.63, 1.62, 1.59, 1.58, 1.58, 1.6, 1.61, 1.62, 1.63, 1.58, 1.64,
|
||||||
|
1.63, 1.62, 1.62, 1.62, 1.62, 1.63, 1.61, 1.57, 1.64, 1.62, 1.61,
|
||||||
|
1.6, 1.58, 1.57, 1.59, 1.61, 1.62, 1.63, 1.58, 1.61, 1.6, 1.62,
|
||||||
|
1.63, 1.6, 1.61, 1.64, 1.64, 1.63, 1.58, 1.6, 1.62, 1.63, 1.65,
|
||||||
|
1.62, 1.58, 1.59, 1.57, 1.58, 1.57, 1.57, 1.58, 1.59, 1.64, 1.61,
|
||||||
|
1.64, 1.62, 1.6, 1.59, 1.65, 1.62, 1.62, 1.6, 1.58, 1.57, 1.59,
|
||||||
|
1.57, 1.59, 1.62, 1.56, 1.57, 1.57, 1.61, 1.62, 1.56, 1.58, 1.59,
|
||||||
|
1.6, 1.62, 1.58, 1.6, 1.6, 1.62, 1.63, 1.58, 1.59, 1.6, 1.63,
|
||||||
|
1.62, 1.58, 1.59, 1.62, 1.63, 1.64, 1.58, 1.59, 1.62, 1.63, 1.61,
|
||||||
|
1.58, 1.59, 1.6, 1.61, 1.63, 1.57, 1.59, 1.61, 1.61, 1.62, 1.58,
|
||||||
|
1.58, 1.6, 1.61, 1.63, 1.62, 1.58, 1.58, 1.58, 1.57, 1.63, 1.59,
|
||||||
|
1.57, 1.58, 1.57, 1.58, 1.62, 1.61, 1.63, 1.61, 1.58, 1.57, 1.59,
|
||||||
|
1.6, 1.62, 1.62, 1.6, 1.6, 1.57, 1.57,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
fourBarValid: {
|
||||||
|
msg: 'fourbar valid',
|
||||||
|
req_code: 'CASE_FB_001',
|
||||||
|
req_from: 'local_debug',
|
||||||
|
req_cmd: 'Cmd_FourBar_CrankSlider',
|
||||||
|
req_param: {
|
||||||
|
L_AB: 0.5,
|
||||||
|
L_BS: 2.0,
|
||||||
|
S_OFS: 0.0,
|
||||||
|
angleDeg: 45.0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
fourBarInvalid: {
|
||||||
|
msg: 'fourbar invalid',
|
||||||
|
req_code: 'CASE_FB_002',
|
||||||
|
req_from: 'local_debug',
|
||||||
|
req_cmd: 'Cmd_FourBar_CrankSlider',
|
||||||
|
req_param: {
|
||||||
|
L_AB: 0.0,
|
||||||
|
L_BS: 0.2,
|
||||||
|
S_OFS: 0.5,
|
||||||
|
angleDeg: 30.0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
quadrupedPoints: {
|
||||||
|
msg: 'quadruped points from motor angles',
|
||||||
|
req_code: 'CASE_QP_001',
|
||||||
|
req_from: 'local_debug',
|
||||||
|
req_cmd: 'Cmd_QuadrupedRobot_CalculateAllPointsFromMotorAngles',
|
||||||
|
req_param: {
|
||||||
|
Param: {
|
||||||
|
LF: {
|
||||||
|
thigh_angle_deg: -37.4784578338516,
|
||||||
|
shank_angle_deg: -22.4917532451628,
|
||||||
|
ankle_angle_deg: 0.0,
|
||||||
|
},
|
||||||
|
LH: {
|
||||||
|
thigh_angle_deg: -34.0564623655754,
|
||||||
|
shank_angle_deg: -22.1353106546916,
|
||||||
|
ankle_angle_deg: 0.0,
|
||||||
|
},
|
||||||
|
RF: {
|
||||||
|
thigh_angle_deg: 34.0564623655754,
|
||||||
|
shank_angle_deg: 22.1353106546916,
|
||||||
|
ankle_angle_deg: 0.0,
|
||||||
|
},
|
||||||
|
RH: {
|
||||||
|
thigh_angle_deg: 37.4784578338516,
|
||||||
|
shank_angle_deg: 22.4917532451628,
|
||||||
|
ankle_angle_deg: 0.0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
quadrupedForwardGait: {
|
||||||
|
msg: 'quadruped forward gait',
|
||||||
|
req_code: 'CASE_QP_002',
|
||||||
|
req_from: 'local_debug',
|
||||||
|
req_cmd: 'Cmd_QuadrupedRobot_PerformForwardKinematics',
|
||||||
|
req_param: {
|
||||||
|
GaitInfo: {
|
||||||
|
GaitType: 'trot',
|
||||||
|
Period: 0.8,
|
||||||
|
Frequency: 50.0,
|
||||||
|
StepTime: 0.02,
|
||||||
|
SupportTime: 0.4,
|
||||||
|
SwingTime: 0.4,
|
||||||
|
SupportRatio: 0.5,
|
||||||
|
SwingRatio: 0.5,
|
||||||
|
TotalFrames: 40,
|
||||||
|
TotalTime: 0.8,
|
||||||
|
},
|
||||||
|
SystemParameters: {
|
||||||
|
A_x: 0.0,
|
||||||
|
A_y: 500.0,
|
||||||
|
L10: 300.0,
|
||||||
|
L20: 286.0,
|
||||||
|
L30: 55.0,
|
||||||
|
StepLength: 60.0,
|
||||||
|
StepHeight: 50.0,
|
||||||
|
X: 30.0,
|
||||||
|
DeltaX: 0.0,
|
||||||
|
L21: 70.0,
|
||||||
|
L22: 292.0,
|
||||||
|
L23: 80.0,
|
||||||
|
Beta1: 166.0,
|
||||||
|
BB2_BC_Angle: 3.3859387489,
|
||||||
|
C1_B_Length: 107.0,
|
||||||
|
CC1: 179.0,
|
||||||
|
L31: 32.0,
|
||||||
|
C2C3_Length: 32.0,
|
||||||
|
Lead: 10.0,
|
||||||
|
D1_L_Offset: 90.0,
|
||||||
|
D2_L_Offset: 50.0,
|
||||||
|
C4_D2_Offset: 18.0,
|
||||||
|
ThighMotorReduction: 1.0,
|
||||||
|
ShankMotorReduction: 1.0,
|
||||||
|
AnkleMotorReduction: 1.0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
function escapeHtml(value) {
|
||||||
|
return value
|
||||||
|
.replaceAll('&', '&')
|
||||||
|
.replaceAll('<', '<')
|
||||||
|
.replaceAll('>', '>');
|
||||||
|
}
|
||||||
|
|
||||||
|
function resetMeta() {
|
||||||
|
metaSuccess.textContent = '未执行';
|
||||||
|
metaSuccess.className = 'response-meta-value';
|
||||||
|
metaCode.textContent = '-';
|
||||||
|
metaCode.className = 'response-meta-value';
|
||||||
|
metaCmd.textContent = '-';
|
||||||
|
metaReq.textContent = '-';
|
||||||
|
}
|
||||||
|
|
||||||
|
function setMetaFromJson(data) {
|
||||||
|
const success =
|
||||||
|
data && typeof data.success === 'boolean' ? data.success : null;
|
||||||
|
metaSuccess.textContent =
|
||||||
|
success === null ? '未知' : success ? '成功' : '失败';
|
||||||
|
metaSuccess.className =
|
||||||
|
'response-meta-value ' +
|
||||||
|
(success ? 'is-ok' : success === false ? 'is-error' : '');
|
||||||
|
|
||||||
|
metaCode.textContent = data && 'code' in data ? String(data.code) : '-';
|
||||||
|
metaCode.className =
|
||||||
|
'response-meta-value ' + (success === false ? 'is-error' : '');
|
||||||
|
|
||||||
|
metaCmd.textContent = data && data.req_cmd ? data.req_cmd : '-';
|
||||||
|
metaReq.textContent = data && data.req_code ? data.req_code : '-';
|
||||||
|
}
|
||||||
|
|
||||||
|
function highlightJson(value) {
|
||||||
|
const escaped = escapeHtml(value);
|
||||||
|
return escaped.replace(
|
||||||
|
/("(?:\\u[a-fA-F0-9]{4}|\\[^u]|[^\\"])*")(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d+)?(?:[eE][+\-]?\d+)?/g,
|
||||||
|
(match, stringToken, keySuffix, literalToken) => {
|
||||||
|
if (stringToken) {
|
||||||
|
if (keySuffix) {
|
||||||
|
return `<span class="json-key">${stringToken}</span>${keySuffix}`;
|
||||||
|
}
|
||||||
|
return `<span class="json-string">${stringToken}</span>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (literalToken === 'true' || literalToken === 'false') {
|
||||||
|
return `<span class="json-boolean">${match}</span>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (literalToken === 'null') {
|
||||||
|
return `<span class="json-null">${match}</span>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return `<span class="json-number">${match}</span>`;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderOutput(value) {
|
||||||
|
if (typeof value !== 'string') {
|
||||||
|
const jsonText = JSON.stringify(value, null, 2);
|
||||||
|
output.innerHTML = highlightJson(jsonText);
|
||||||
|
setMetaFromJson(value);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const parsed = JSON.parse(value);
|
||||||
|
renderOutput(parsed);
|
||||||
|
} catch {
|
||||||
|
resetMeta();
|
||||||
|
output.textContent = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function allocString(value) {
|
||||||
|
const size = wasmModule.lengthBytesUTF8(value) + 1;
|
||||||
|
const ptr = wasmModule._malloc(size);
|
||||||
|
wasmModule.stringToUTF8(value, ptr, size);
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadSelectedSample() {
|
||||||
|
const selectedKey = sampleSelect.value;
|
||||||
|
const sample = requestSamples[selectedKey];
|
||||||
|
if (!sample) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
requestBox.value = JSON.stringify(sample, null, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function ensureModule() {
|
||||||
|
if (wasmModule) {
|
||||||
|
return wasmModule;
|
||||||
|
}
|
||||||
|
|
||||||
|
requestStatus.textContent = '正在加载模块';
|
||||||
|
wasmModule = await createSmartMathModule();
|
||||||
|
moduleStatus.textContent = 'WASM 已加载';
|
||||||
|
requestStatus.textContent = '模块加载完成';
|
||||||
|
return wasmModule;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function initializeBusinessApi() {
|
||||||
|
const mod = await ensureModule();
|
||||||
|
let responsePtr = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
responsePtr = mod._init_func();
|
||||||
|
const text = mod.UTF8ToString(responsePtr);
|
||||||
|
renderOutput(text);
|
||||||
|
moduleStatus.textContent = '业务接口已初始化';
|
||||||
|
requestStatus.textContent = '初始化成功';
|
||||||
|
} catch (error) {
|
||||||
|
moduleStatus.textContent = '初始化失败';
|
||||||
|
requestStatus.textContent = '初始化失败';
|
||||||
|
resetMeta();
|
||||||
|
output.textContent = String(
|
||||||
|
error && error.stack ? error.stack : error,
|
||||||
|
);
|
||||||
|
} finally {
|
||||||
|
if (responsePtr) {
|
||||||
|
mod._smart_free_string(responsePtr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function runRequest() {
|
||||||
|
const mod = await ensureModule();
|
||||||
|
let requestPtr = null;
|
||||||
|
let responsePtr = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const raw = requestBox.value.trim();
|
||||||
|
JSON.parse(raw);
|
||||||
|
requestPtr = allocString(raw);
|
||||||
|
responsePtr = mod._func(requestPtr);
|
||||||
|
const text = mod.UTF8ToString(responsePtr);
|
||||||
|
renderOutput(text);
|
||||||
|
requestStatus.textContent = '请求成功';
|
||||||
|
} catch (error) {
|
||||||
|
requestStatus.textContent = '请求失败';
|
||||||
|
resetMeta();
|
||||||
|
output.textContent = String(
|
||||||
|
error && error.stack ? error.stack : error,
|
||||||
|
);
|
||||||
|
} finally {
|
||||||
|
if (requestPtr) {
|
||||||
|
mod._free(requestPtr);
|
||||||
|
}
|
||||||
|
if (responsePtr) {
|
||||||
|
mod._smart_free_string(responsePtr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
document
|
||||||
|
.getElementById('init-btn')
|
||||||
|
.addEventListener('click', initializeBusinessApi);
|
||||||
|
document.getElementById('run-btn').addEventListener('click', runRequest);
|
||||||
|
document
|
||||||
|
.getElementById('sample-btn')
|
||||||
|
.addEventListener('click', loadSelectedSample);
|
||||||
|
document.getElementById('clear-btn').addEventListener('click', () => {
|
||||||
|
requestStatus.textContent = '等待操作';
|
||||||
|
resetMeta();
|
||||||
|
output.textContent = 'No output yet.';
|
||||||
|
});
|
||||||
|
|
||||||
|
resetMeta();
|
||||||
|
loadSelectedSample();
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
11
public/smart_test.html
Normal file
11
public/smart_test.html
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="zh-CN">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta http-equiv="refresh" content="0; url=./index.html" />
|
||||||
|
<title>Redirecting</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<p>Redirecting to <a href="./index.html">index.html</a> ...</p>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -74,7 +74,11 @@ $startTime = Get-Date
|
|||||||
|
|
||||||
# 编译命令 - 使用当前真实业务模块源码
|
# 编译命令 - 使用当前真实业务模块源码
|
||||||
$compileResult = emcc `
|
$compileResult = emcc `
|
||||||
src/KinematicsWebAPI.cpp `
|
src/api/KinematicsWebAPI.Core.cpp `
|
||||||
|
src/api/KinematicsWebAPI.RobotCommands.cpp `
|
||||||
|
src/api/KinematicsWebAPI.SpcCommands.cpp `
|
||||||
|
src/api/KinematicsWebAPI.FourBarCommands.cpp `
|
||||||
|
src/api/KinematicsWebAPI.QuadrupedCommands.cpp `
|
||||||
src/main.cpp `
|
src/main.cpp `
|
||||||
src/Robot.cpp `
|
src/Robot.cpp `
|
||||||
src/spc_core.cpp `
|
src/spc_core.cpp `
|
||||||
|
|||||||
@@ -1,67 +1,55 @@
|
|||||||
# serve.ps1 - 开发服务器启动脚本
|
# serve.ps1 - development server launcher
|
||||||
Write-Host "=== 启动开发服务器 ===" -ForegroundColor Cyan
|
Write-Host "=== Start Dev Server ===" -ForegroundColor Cyan
|
||||||
Write-Host ""
|
Write-Host ""
|
||||||
|
|
||||||
# 检查是否已构建
|
|
||||||
if (-not (Test-Path "public/wasm/smart_math.js")) {
|
if (-not (Test-Path "public/wasm/smart_math.js")) {
|
||||||
Write-Host "[!] 未找到 WASM 文件,正在构建..." -ForegroundColor Yellow
|
Write-Host "[!] WASM output not found, building first..." -ForegroundColor Yellow
|
||||||
& "$PSScriptRoot\build.ps1"
|
& "$PSScriptRoot\build.ps1"
|
||||||
|
|
||||||
if (-not (Test-Path "public/wasm/smart_math.js")) {
|
if (-not (Test-Path "public/wasm/smart_math.js")) {
|
||||||
Write-Host "[✗] 构建失败,无法启动服务器" -ForegroundColor Red
|
Write-Host "[x] Build failed, cannot start server" -ForegroundColor Red
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
# 检查 Python
|
$pythonCommand = $null
|
||||||
try {
|
$pythonCandidates = @(
|
||||||
python --version 2>&1 | Out-Null
|
|
||||||
$hasPython = $true
|
|
||||||
} catch {
|
|
||||||
$hasPython = $false
|
|
||||||
}
|
|
||||||
|
|
||||||
if (-not $hasPython) {
|
|
||||||
Write-Host "[!] 未找到 Python,尝试使用系统 Python..." -ForegroundColor Yellow
|
|
||||||
|
|
||||||
# 尝试常见的 Python 路径
|
|
||||||
$pythonPaths = @(
|
|
||||||
"python",
|
"python",
|
||||||
"python3",
|
|
||||||
"py",
|
"py",
|
||||||
|
"python3",
|
||||||
"C:\Python39\python.exe",
|
"C:\Python39\python.exe",
|
||||||
"C:\Python38\python.exe",
|
"C:\Python38\python.exe",
|
||||||
"C:\Python37\python.exe"
|
"C:\Python37\python.exe"
|
||||||
)
|
)
|
||||||
|
|
||||||
foreach ($path in $pythonPaths) {
|
foreach ($candidate in $pythonCandidates) {
|
||||||
try {
|
try {
|
||||||
& $path --version 2>&1 | Out-Null
|
& $candidate --version 2>&1 | Out-Null
|
||||||
$env:PATH = "$(Split-Path $path -ErrorAction SilentlyContinue);$env:PATH"
|
if ($LASTEXITCODE -eq 0) {
|
||||||
$hasPython = $true
|
$pythonCommand = $candidate
|
||||||
Write-Host "[✓] 找到 Python: $path" -ForegroundColor Green
|
Write-Host "[ok] Python found: $candidate" -ForegroundColor Green
|
||||||
break
|
break
|
||||||
|
}
|
||||||
} catch {
|
} catch {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (-not $hasPython) {
|
if (-not $pythonCommand) {
|
||||||
Write-Host "[✗] 未找到 Python,请安装 Python 3.7+ 并添加到 PATH" -ForegroundColor Red
|
Write-Host "[x] Python 3.7+ not found in PATH" -ForegroundColor Red
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
|
|
||||||
# 启动服务器
|
$publicDir = Resolve-Path "public"
|
||||||
Write-Host "[ ] 启动开发服务器..." -ForegroundColor Cyan
|
|
||||||
Write-Host ""
|
|
||||||
Write-Host "🌐 服务器信息:" -ForegroundColor White
|
|
||||||
Write-Host "├─ 地址: http://localhost:8080" -ForegroundColor Cyan
|
|
||||||
Write-Host "├─ 目录: $(Resolve-Path "public")" -ForegroundColor Gray
|
|
||||||
Write-Host "├─ 主页: /smart_test.html" -ForegroundColor Gray
|
|
||||||
Write-Host "└─ 按 Ctrl+C 停止服务器" -ForegroundColor Yellow
|
|
||||||
Write-Host ""
|
|
||||||
Write-Host ("─" * 50) -ForegroundColor DarkGray
|
|
||||||
|
|
||||||
Set-Location "public"
|
Write-Host "[ ] Starting dev server..." -ForegroundColor Cyan
|
||||||
python -m http.server 8080
|
Write-Host ""
|
||||||
|
Write-Host "Server info:" -ForegroundColor White
|
||||||
|
Write-Host " - URL: http://localhost:8080/index.html" -ForegroundColor Cyan
|
||||||
|
Write-Host " - Root: $publicDir" -ForegroundColor Gray
|
||||||
|
Write-Host " - Home: /index.html" -ForegroundColor Gray
|
||||||
|
Write-Host " - Stop: Ctrl+C" -ForegroundColor Yellow
|
||||||
|
Write-Host ""
|
||||||
|
|
||||||
|
Set-Location $publicDir
|
||||||
|
& $pythonCommand -m http.server 8080
|
||||||
|
|||||||
@@ -1,609 +0,0 @@
|
|||||||
|
|
||||||
#include "QuadrupedRobotSimulation/KinematicsHelper.h"
|
|
||||||
#include "KinematicsWebAPI.h"
|
|
||||||
#include "FourBarMechanism/CrankSliderMechanism.h"
|
|
||||||
#include "URDFStrings.h"
|
|
||||||
|
|
||||||
#include <iostream>
|
|
||||||
#include <thread>
|
|
||||||
#include <chrono>
|
|
||||||
#include <iomanip>
|
|
||||||
#include <sstream>
|
|
||||||
#include <codecvt>
|
|
||||||
#include <locale>
|
|
||||||
#include <csignal>
|
|
||||||
|
|
||||||
// inversePoseStr2PSteps
|
|
||||||
KinematicsWebAPI::KinematicsWebAPI()
|
|
||||||
{
|
|
||||||
init_func();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string KinematicsWebAPI::init_func()
|
|
||||||
{
|
|
||||||
std::string response_string;
|
|
||||||
std::string urdf_string = URDFStrings::abb120_urdf; // 使用新的命名空间访问
|
|
||||||
auto result = RobotManager::initRobot(urdf_string, "9D7EAEF4-1AAB-499E-8783-B6CE016BC6D1");
|
|
||||||
auto result1 = RobotManager::initRobot(urdf_string, "abb_irb120_3_58");
|
|
||||||
|
|
||||||
json error_response = utils::create_api_response(true, 200, "init_func", "", "", "");
|
|
||||||
response_string = error_response.dump();
|
|
||||||
return response_string;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string KinematicsWebAPI::func(std::string sanitized_body)
|
|
||||||
{
|
|
||||||
|
|
||||||
// 添加调试输出
|
|
||||||
log("func called with body: " + sanitized_body.substr(0, 100));
|
|
||||||
|
|
||||||
assert(!sanitized_body.empty() && "sanitized_body should not be empty");
|
|
||||||
std::string response_string;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
json request_json;
|
|
||||||
// 构建响应数据
|
|
||||||
json result;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
request_json = json::parse(sanitized_body);
|
|
||||||
}
|
|
||||||
catch (const std::exception &e)
|
|
||||||
{
|
|
||||||
json error_response = utils::create_api_response(false, 400, "Invalid JSON format", "", "", "");
|
|
||||||
response_string = error_response.dump();
|
|
||||||
return response_string;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string msg = request_json.value("msg", "");
|
|
||||||
std::string req_code = request_json.value("req_code", "");
|
|
||||||
std::string req_from = request_json.value("req_from", "");
|
|
||||||
std::string req_cmd = request_json.value("req_cmd", "");
|
|
||||||
json req_param = request_json.value("req_param", json::object());
|
|
||||||
|
|
||||||
json res_data;
|
|
||||||
|
|
||||||
// 使用if语句直接处理对应的命令,不通过中间函数
|
|
||||||
if (req_cmd == "Cmd_Kinematics_inverse_pose_str")
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
log("Handling Cmd_Kinematics_inverse_pose_str command");
|
|
||||||
|
|
||||||
std::string pose_str = req_param.value("pose_str", "");
|
|
||||||
std::string q_init_str = req_param.value("q_init_str", "0,0,0,0,0,0");
|
|
||||||
std::string robot_uuid = req_param.value("robot_uuid", "default");
|
|
||||||
|
|
||||||
auto robot = RobotManager::getRobot(robot_uuid);
|
|
||||||
if (!robot || !robot->isInitialized())
|
|
||||||
{
|
|
||||||
res_data = {{"error", "Robot not found or not initialized"}};
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
result = robot->inversePoseStr(pose_str, q_init_str);
|
|
||||||
if (result.empty())
|
|
||||||
{
|
|
||||||
res_data = {{"error", "Inverse kinematics calculation failed"}};
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
res_data = {{"joints", result}, {"success", true}};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (const std::exception &e)
|
|
||||||
{
|
|
||||||
res_data = {{"error", "Failed to calculate inverse kinematics: " + std::string(e.what())}};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (req_cmd == "Cmd_Kinematics_inverse_pose_str_2PSteps")
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
log("Handling Cmd_Kinematics_inverse_pose_str_2PSteps command");
|
|
||||||
|
|
||||||
std::string pose_str = req_param.value("pose_str", "");
|
|
||||||
std::string q_init_str = req_param.value("q_init_str", "0,0,0,0,0,0");
|
|
||||||
std::string robot_uuid = req_param.value("robot_uuid", "default");
|
|
||||||
std::string steps_str = req_param.value("steps_str", "default");
|
|
||||||
int steps = std::stoi(steps_str);
|
|
||||||
auto robot = RobotManager::getRobot(robot_uuid);
|
|
||||||
if (!robot || !robot->isInitialized())
|
|
||||||
{
|
|
||||||
res_data = {{"error", "Robot not found or not initialized"}};
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
result = robot->inversePoseStr2PSteps(pose_str, q_init_str, steps);
|
|
||||||
if (result.empty())
|
|
||||||
{
|
|
||||||
res_data = {{"error", "Inverse kinematics calculation failed"}};
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
res_data = {{"joints", result}, {"success", true}};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (const std::exception &e)
|
|
||||||
{
|
|
||||||
res_data = {{"error", "Failed to calculate inverse kinematics: " + std::string(e.what())}};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (req_cmd == "Cmd_Kinematics_inverse_pose_str_NoDifference")
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
log("Handling Cmd_Kinematics_inverse_pose_strNoDifference command");
|
|
||||||
|
|
||||||
std::string pose_str = req_param.value("pose_str", "");
|
|
||||||
std::string q_init_str = req_param.value("q_init_str", "0,0,0,0,0,0");
|
|
||||||
std::string robot_uuid = req_param.value("robot_uuid", "default");
|
|
||||||
|
|
||||||
auto robot = RobotManager::getRobot(robot_uuid);
|
|
||||||
if (!robot || !robot->isInitialized())
|
|
||||||
{
|
|
||||||
res_data = {{"error", "Robot not found or not initialized"}};
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
result = robot->inversePoseStrNoDifference(pose_str, q_init_str);
|
|
||||||
if (result.empty())
|
|
||||||
{
|
|
||||||
res_data = {{"error", "Inverse kinematics calculation failed"}};
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
res_data = {{"joints", result}, {"success", true}};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (const std::exception &e)
|
|
||||||
{
|
|
||||||
res_data = {{"error", "Failed to calculate inverse kinematics: " + std::string(e.what())}};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (req_cmd == "Cmd_Kinematics_forward_pose_str")
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
log("Handling Cmd_Kinematics_forward_pose_str command");
|
|
||||||
|
|
||||||
std::string joints_str = req_param.value("q_init_str", "0,0,0,0,0,0");
|
|
||||||
std::string robot_uuid = req_param.value("robot_uuid", "default");
|
|
||||||
|
|
||||||
auto robot = RobotManager::getRobot(robot_uuid);
|
|
||||||
if (!robot || !robot->isInitialized())
|
|
||||||
{
|
|
||||||
res_data = {{"error", "Robot not found or not initialized"}};
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
auto joints = robot->parseJointString(joints_str);
|
|
||||||
if (joints.size() != 6)
|
|
||||||
{
|
|
||||||
res_data = {{"error", "Invalid joints format"}};
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
double tcp_pose[7];
|
|
||||||
if (robot->calculateFK_TCP(joints.data(), tcp_pose))
|
|
||||||
{
|
|
||||||
res_data = {
|
|
||||||
{"position", {tcp_pose[0], tcp_pose[1], tcp_pose[2]}},
|
|
||||||
{"orientation", {tcp_pose[3], tcp_pose[4], tcp_pose[5], tcp_pose[6]}},
|
|
||||||
{"joints", joints},
|
|
||||||
{"success", true}};
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
res_data = {{"error", "Forward kinematics calculation failed"}};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (const std::exception &e)
|
|
||||||
{
|
|
||||||
res_data = {{"error", "Failed to calculate forward kinematics: " + std::string(e.what())}};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (req_cmd == "Cmd_SelectCraftTree")
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
log("Handling Cmd_SelectCraftTree command");
|
|
||||||
std::string tree_id = req_param.value("tree_id", "");
|
|
||||||
res_data = {
|
|
||||||
{"success", true},
|
|
||||||
{"tree_id", tree_id},
|
|
||||||
{"message", "Craft tree selected successfully"},
|
|
||||||
{"timestamp", utils::get_current_time()}};
|
|
||||||
}
|
|
||||||
catch (const std::exception &e)
|
|
||||||
{
|
|
||||||
res_data = {{"error", "Failed to select craft tree: " + std::string(e.what())}};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (req_cmd == "Cmd_AddOperationTree")
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
log("Handling Cmd_AddOperationTree command");
|
|
||||||
std::string tree_name = req_param.value("name", "");
|
|
||||||
json operations = req_param.value("operations", json::array());
|
|
||||||
|
|
||||||
res_data = {
|
|
||||||
{"success", true},
|
|
||||||
{"tree_name", tree_name},
|
|
||||||
{"operations_count", operations.size()},
|
|
||||||
{"message", "Operation tree added successfully"},
|
|
||||||
{"timestamp", utils::get_current_time()}};
|
|
||||||
}
|
|
||||||
catch (const std::exception &e)
|
|
||||||
{
|
|
||||||
res_data = {{"error", "Failed to add operation tree: " + std::string(e.what())}};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (req_cmd == "Cmd_InitRobot")
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
log("Handling Cmd_InitRobot command");
|
|
||||||
std::string urdf_base64 = req_param.value("urdf_base64", "");
|
|
||||||
std::string uuid = req_param.value("robot_uuid", "");
|
|
||||||
bool force_update = req_param.value("force_update", true);
|
|
||||||
|
|
||||||
std::string urdf_content = utils::base64_to_urdf(urdf_base64);
|
|
||||||
|
|
||||||
if (!utils::validate_urdf_base64(urdf_base64))
|
|
||||||
{
|
|
||||||
res_data = {
|
|
||||||
{"success", false},
|
|
||||||
{"message", "Invalid URDF format"},
|
|
||||||
{"timestamp", utils::get_current_time()}};
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
auto result = RobotManager::initRobot(urdf_content, uuid, force_update);
|
|
||||||
res_data = {
|
|
||||||
{"success", result.first},
|
|
||||||
{"message", result.second},
|
|
||||||
{"timestamp", utils::get_current_time()}};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (const std::exception &e)
|
|
||||||
{
|
|
||||||
res_data = {{"error", "Failed to initialize robot: " + std::string(e.what())}};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (req_cmd == "Cmd_GetRobot")
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
log("Handling Cmd_GetRobot command");
|
|
||||||
|
|
||||||
std::string uuid = req_param.value("uuid", "");
|
|
||||||
auto robot = RobotManager::getRobot(uuid);
|
|
||||||
|
|
||||||
if (!robot)
|
|
||||||
{
|
|
||||||
res_data = {{"error", "Robot not found"}};
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
res_data = {
|
|
||||||
{"success", true},
|
|
||||||
{"uuid", uuid},
|
|
||||||
{"initialized", robot->isInitialized()},
|
|
||||||
{"joints_count", robot->getNumberOfJoints()},
|
|
||||||
{"timestamp", utils::get_current_time()}};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (const std::exception &e)
|
|
||||||
{
|
|
||||||
res_data = {{"error", "Failed to get robot: " + std::string(e.what())}};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (req_cmd == "Cmd_RemoveRobot")
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
log("Handling Cmd_RemoveRobot command");
|
|
||||||
|
|
||||||
std::string uuid = req_param.value("uuid", "");
|
|
||||||
auto result = RobotManager::removeRobot(uuid);
|
|
||||||
|
|
||||||
res_data = {
|
|
||||||
{"success", result.first},
|
|
||||||
{"message", result.second},
|
|
||||||
{"timestamp", utils::get_current_time()}};
|
|
||||||
}
|
|
||||||
catch (const std::exception &e)
|
|
||||||
{
|
|
||||||
res_data = {{"error", "Failed to remove robot: " + std::string(e.what())}};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (req_cmd == "Cmd_ListRobots")
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
log("Handling Cmd_ListRobots command");
|
|
||||||
|
|
||||||
bool detail = req_param.value("detail", false);
|
|
||||||
auto robots = RobotManager::listRobots(detail);
|
|
||||||
|
|
||||||
res_data = {
|
|
||||||
{"success", true},
|
|
||||||
{"robots", robots},
|
|
||||||
{"count", robots.size()},
|
|
||||||
{"timestamp", utils::get_current_time()}};
|
|
||||||
}
|
|
||||||
catch (const std::exception &e)
|
|
||||||
{
|
|
||||||
res_data = {{"error", "Failed to list robots: " + std::string(e.what())}};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (req_cmd == "Cmd_Kinematics_forward_all_joints")
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
log("Handling Cmd_Kinematics_forward_all_joints command");
|
|
||||||
|
|
||||||
std::string joints_str = req_param.value("joints_str", "0,0,0,0,0,0");
|
|
||||||
std::string robot_uuid = req_param.value("robot_uuid", "default");
|
|
||||||
|
|
||||||
auto robot = RobotManager::getRobot(robot_uuid);
|
|
||||||
if (!robot || !robot->isInitialized())
|
|
||||||
{
|
|
||||||
res_data = {{"error", "Robot not found or not initialized"}};
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
auto joints_poses_array = robot->handleKinematicsForwardAllJoints(joints_str);
|
|
||||||
res_data = {
|
|
||||||
{"OPERATION", joints_poses_array},
|
|
||||||
{"success", true}};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (const std::exception &e)
|
|
||||||
{
|
|
||||||
res_data = {{"error", "Failed to calculate forward kinematics for all joints: " + std::string(e.what())}};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (req_cmd == "Cmd_Spc")
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
log("Handling " + req_cmd + " command");
|
|
||||||
json result = SpcCalculator::Spc(req_param);
|
|
||||||
res_data = result;
|
|
||||||
}
|
|
||||||
catch (const std::exception &e)
|
|
||||||
{
|
|
||||||
res_data = {{"error", "Failed to Cmd_Spc: " + std::string(e.what())}};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (req_cmd == "Cmd_FourBar_CrankSlider")
|
|
||||||
{
|
|
||||||
// 请求参数示例:
|
|
||||||
// {
|
|
||||||
// "L_AB": 0.5,
|
|
||||||
// "L_BS": 2.0,
|
|
||||||
// "S_OFS": 0.0,
|
|
||||||
// "angleDeg": 45.0
|
|
||||||
// }
|
|
||||||
try
|
|
||||||
{
|
|
||||||
log("Handling " + req_cmd + " command");
|
|
||||||
|
|
||||||
// 从请求参数中提取值
|
|
||||||
double L_AB = req_param.value("L_AB", 0.5);
|
|
||||||
double L_BS = req_param.value("L_BS", 2.0);
|
|
||||||
double S_OFS = req_param.value("S_OFS", 0.0);
|
|
||||||
double angleDeg = req_param.value("angleDeg", 0.0);
|
|
||||||
|
|
||||||
log("Parameters: L_AB=" + std::to_string(L_AB) +
|
|
||||||
", L_BS=" + std::to_string(L_BS) +
|
|
||||||
", S_OFS=" + std::to_string(S_OFS) +
|
|
||||||
", angleDeg=" + std::to_string(angleDeg));
|
|
||||||
|
|
||||||
// 创建曲柄滑块机构实例
|
|
||||||
std::unique_ptr<CrankSliderMechanism> mechanism(createCrankSliderMechanism());
|
|
||||||
|
|
||||||
// 设置连杆参数
|
|
||||||
mechanism->setL_AB(L_AB);
|
|
||||||
mechanism->setL_BS(L_BS);
|
|
||||||
mechanism->setS_OFS(S_OFS);
|
|
||||||
|
|
||||||
// 验证参数
|
|
||||||
ValidationResult validation = mechanism->validateParameters();
|
|
||||||
if (!validation.isValid())
|
|
||||||
{
|
|
||||||
res_data = {
|
|
||||||
{"success", false},
|
|
||||||
{"error", "Invalid parameters"},
|
|
||||||
{"validation_errors", validation.Errors},
|
|
||||||
{"validation_warnings", validation.Warnings}};
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// 执行计算
|
|
||||||
MechanismState state = mechanism->calculate(angleDeg);
|
|
||||||
|
|
||||||
// 检查是否有错误
|
|
||||||
if (state.hasError())
|
|
||||||
{
|
|
||||||
res_data = {
|
|
||||||
{"success", false},
|
|
||||||
{"error", state.ErrorMessage}};
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// 构建响应数据
|
|
||||||
json result;
|
|
||||||
result["success"] = true;
|
|
||||||
|
|
||||||
// 添加点坐标
|
|
||||||
json points_json;
|
|
||||||
for (const auto &point_pair : state.Points)
|
|
||||||
{
|
|
||||||
json point;
|
|
||||||
point["x"] = point_pair.second.X;
|
|
||||||
point["y"] = point_pair.second.Y;
|
|
||||||
points_json[point_pair.first] = point;
|
|
||||||
}
|
|
||||||
result["points"] = points_json;
|
|
||||||
|
|
||||||
// 添加姿态信息
|
|
||||||
json poses_json;
|
|
||||||
for (const auto &pose_pair : state.Poses)
|
|
||||||
{
|
|
||||||
json pose;
|
|
||||||
pose["tx"] = pose_pair.second.tx;
|
|
||||||
pose["ty"] = pose_pair.second.ty;
|
|
||||||
pose["tz"] = pose_pair.second.tz;
|
|
||||||
pose["qx"] = pose_pair.second.qx;
|
|
||||||
pose["qy"] = pose_pair.second.qy;
|
|
||||||
pose["qz"] = pose_pair.second.qz;
|
|
||||||
pose["qw"] = pose_pair.second.qw;
|
|
||||||
poses_json[pose_pair.first] = pose;
|
|
||||||
}
|
|
||||||
result["poses"] = poses_json;
|
|
||||||
|
|
||||||
// 添加角度信息
|
|
||||||
json angles_json;
|
|
||||||
for (const auto &angle_pair : state.Angles)
|
|
||||||
{
|
|
||||||
angles_json[angle_pair.first] = angle_pair.second;
|
|
||||||
}
|
|
||||||
result["angles"] = angles_json;
|
|
||||||
|
|
||||||
// 添加输入值
|
|
||||||
result["input_value"] = state.InputValue;
|
|
||||||
|
|
||||||
// 添加警告信息(如果有)
|
|
||||||
if (state.hasWarning())
|
|
||||||
{
|
|
||||||
result["warning"] = state.WarningMessage;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 添加轨迹信息
|
|
||||||
std::vector<Vector2D> trajectory = mechanism->getTrajectoryPoints();
|
|
||||||
std::vector<Vector2D> slider_trajectory = mechanism->getSliderTrajectory();
|
|
||||||
|
|
||||||
json trajectory_json;
|
|
||||||
for (size_t i = 0; i < trajectory.size(); i++)
|
|
||||||
{
|
|
||||||
json point;
|
|
||||||
point["x"] = trajectory[i].X;
|
|
||||||
point["y"] = trajectory[i].Y;
|
|
||||||
trajectory_json.push_back(point);
|
|
||||||
}
|
|
||||||
result["trajectory"] = trajectory_json;
|
|
||||||
|
|
||||||
json slider_trajectory_json;
|
|
||||||
for (size_t i = 0; i < slider_trajectory.size(); i++)
|
|
||||||
{
|
|
||||||
json point;
|
|
||||||
point["x"] = slider_trajectory[i].X;
|
|
||||||
point["y"] = slider_trajectory[i].Y;
|
|
||||||
slider_trajectory_json.push_back(point);
|
|
||||||
}
|
|
||||||
result["slider_trajectory"] = slider_trajectory_json;
|
|
||||||
|
|
||||||
// 添加参数信息
|
|
||||||
result["parameters"] = {
|
|
||||||
{"L_AB", L_AB},
|
|
||||||
{"L_BS", L_BS},
|
|
||||||
{"S_OFS", S_OFS},
|
|
||||||
{"angleDeg", angleDeg}};
|
|
||||||
|
|
||||||
res_data = result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (const std::exception &e)
|
|
||||||
{
|
|
||||||
res_data = {
|
|
||||||
{"success", false},
|
|
||||||
{"error", "Failed to Cmd_FourBar_CrankSlider: " + std::string(e.what())}};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (req_cmd == "Cmd_QuadrupedRobot_CalculateAllPointsFromMotorAngles")
|
|
||||||
{
|
|
||||||
|
|
||||||
// json j = json::parse(jsonStr);
|
|
||||||
std::cout << "[DEBUG 0001] Cmd_QuadrupedRobot_CalculateAllPointsFromMotorAngles 步骤1: 初始化数据结构" << std::endl;
|
|
||||||
std::string jsonInput = req_param.dump();
|
|
||||||
result = KinematicsHelper::QuadrupedRobot_CalculateAllPointsFromMotorAngles(jsonInput);
|
|
||||||
// if (req_param.empty())
|
|
||||||
// {
|
|
||||||
// result = KinematicsHelper::QuadrupedRobot_CalculateAllPointsFromMotorAngles();
|
|
||||||
// }
|
|
||||||
// else
|
|
||||||
// {
|
|
||||||
// std::string jsonInput = req_param.dump();
|
|
||||||
// result = KinematicsHelper::QuadrupedRobot_CalculateAllPointsFromMotorAngles(jsonInput);
|
|
||||||
// }
|
|
||||||
|
|
||||||
res_data = result;
|
|
||||||
}
|
|
||||||
else if (req_cmd == "Cmd_QuadrupedRobot_PerformForwardKinematics")
|
|
||||||
{
|
|
||||||
std::cout << "[DEBUG] Cmd_QuadrupedRobot_PerformForwardKinematics = " << std::endl;
|
|
||||||
std::string jsonInput = req_param.dump();
|
|
||||||
result = KinematicsHelper::QuadrupedRobot_PerformForwardKinematics(jsonInput);
|
|
||||||
// if (req_param.empty())
|
|
||||||
// {
|
|
||||||
// result = KinematicsHelper::QuadrupedRobot_PerformForwardKinematics();
|
|
||||||
// }
|
|
||||||
// else
|
|
||||||
// {
|
|
||||||
// std::string jsonInput = req_param.dump();
|
|
||||||
// result = KinematicsHelper::QuadrupedRobot_PerformForwardKinematics(jsonInput);
|
|
||||||
// }
|
|
||||||
|
|
||||||
res_data = result;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// 未知命令,调用默认处理函数
|
|
||||||
res_data["success"] = false;
|
|
||||||
res_data["error"] = "Unknown command: " + req_cmd;
|
|
||||||
res_data["received_params"] = req_param;
|
|
||||||
res_data["timestamp"] = std::time(nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
json response = utils::create_api_response(true, 0, msg, req_code, req_from, req_cmd, res_data);
|
|
||||||
response_string = response.dump();
|
|
||||||
}
|
|
||||||
catch (const std::exception &e)
|
|
||||||
{
|
|
||||||
json error_response = utils::create_api_response(false, 500, "Processing error: " + std::string(e.what()), "", "", "");
|
|
||||||
response_string = error_response.dump();
|
|
||||||
}
|
|
||||||
|
|
||||||
return response_string;
|
|
||||||
}
|
|
||||||
|
|
||||||
void KinematicsWebAPI::log(const std::string &message)
|
|
||||||
{
|
|
||||||
std::cout << "[" << getCurrentTimestamp() << "] " << message << std::endl;
|
|
||||||
|
|
||||||
if (onLog)
|
|
||||||
{
|
|
||||||
onLog("[" + getCurrentTimestamp() + "] " + message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string KinematicsWebAPI::getCurrentTimestamp()
|
|
||||||
{
|
|
||||||
return utils::get_current_timestamp();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool KinematicsWebAPI::is_running() const
|
|
||||||
{
|
|
||||||
return running_;
|
|
||||||
}
|
|
||||||
333
src/Robot.cpp
333
src/Robot.cpp
File diff suppressed because it is too large
Load Diff
126
src/api/KinematicsWebAPI.Core.cpp
Normal file
126
src/api/KinematicsWebAPI.Core.cpp
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
#include "KinematicsWebAPI.h"
|
||||||
|
#include "URDFStrings.h"
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
#include <ctime>
|
||||||
|
#include <iostream>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
bool isRobotCommand(const std::string &req_cmd)
|
||||||
|
{
|
||||||
|
return req_cmd == "Cmd_Kinematics_inverse_pose_str" ||
|
||||||
|
req_cmd == "Cmd_Kinematics_inverse_pose_str_2PSteps" ||
|
||||||
|
req_cmd == "Cmd_Kinematics_inverse_pose_str_NoDifference" ||
|
||||||
|
req_cmd == "Cmd_Kinematics_forward_pose_str" ||
|
||||||
|
req_cmd == "Cmd_Kinematics_forward_all_joints" ||
|
||||||
|
req_cmd == "Cmd_InitRobot" ||
|
||||||
|
req_cmd == "Cmd_GetRobot" ||
|
||||||
|
req_cmd == "Cmd_RemoveRobot" ||
|
||||||
|
req_cmd == "Cmd_ListRobots" ||
|
||||||
|
req_cmd == "Cmd_SelectCraftTree" ||
|
||||||
|
req_cmd == "Cmd_AddOperationTree";
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
KinematicsWebAPI::KinematicsWebAPI()
|
||||||
|
{
|
||||||
|
init_func();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string KinematicsWebAPI::init_func()
|
||||||
|
{
|
||||||
|
std::string urdf_string = URDFStrings::abb120_urdf;
|
||||||
|
RobotManager::initRobot(urdf_string, "9D7EAEF4-1AAB-499E-8783-B6CE016BC6D1");
|
||||||
|
RobotManager::initRobot(urdf_string, "abb_irb120_3_58");
|
||||||
|
|
||||||
|
return utils::create_api_response(true, 200, "init_func", "", "", "").dump();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string KinematicsWebAPI::func(std::string sanitized_body)
|
||||||
|
{
|
||||||
|
log("func called with body: " + sanitized_body.substr(0, 100));
|
||||||
|
assert(!sanitized_body.empty() && "sanitized_body should not be empty");
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
json request_json;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
request_json = json::parse(sanitized_body);
|
||||||
|
}
|
||||||
|
catch (const std::exception &)
|
||||||
|
{
|
||||||
|
return utils::create_api_response(false, 400, "Invalid JSON format", "", "", "").dump();
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string msg = request_json.value("msg", "");
|
||||||
|
const std::string req_code = request_json.value("req_code", "");
|
||||||
|
const std::string req_from = request_json.value("req_from", "");
|
||||||
|
const std::string req_cmd = request_json.value("req_cmd", "");
|
||||||
|
const json req_param = request_json.value("req_param", json::object());
|
||||||
|
|
||||||
|
json res_data = dispatchCommand(req_cmd, req_param);
|
||||||
|
return utils::create_api_response(true, 0, msg, req_code, req_from, req_cmd, res_data).dump();
|
||||||
|
}
|
||||||
|
catch (const std::exception &e)
|
||||||
|
{
|
||||||
|
return utils::create_api_response(false, 500, "Processing error: " + std::string(e.what()), "", "", "").dump();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
json KinematicsWebAPI::dispatchCommand(const std::string &req_cmd, const json &req_param)
|
||||||
|
{
|
||||||
|
if (isRobotCommand(req_cmd))
|
||||||
|
{
|
||||||
|
return handleRobotCommand(req_cmd, req_param);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (req_cmd == "Cmd_Spc")
|
||||||
|
{
|
||||||
|
return handleSpcCommand(req_cmd, req_param);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (req_cmd == "Cmd_FourBar_CrankSlider")
|
||||||
|
{
|
||||||
|
return handleFourBarCommand(req_cmd, req_param);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (req_cmd == "Cmd_QuadrupedRobot_CalculateAllPointsFromMotorAngles" ||
|
||||||
|
req_cmd == "Cmd_QuadrupedRobot_PerformForwardKinematics")
|
||||||
|
{
|
||||||
|
return handleQuadrupedCommand(req_cmd, req_param);
|
||||||
|
}
|
||||||
|
|
||||||
|
return createUnknownCommandResponse(req_cmd, req_param);
|
||||||
|
}
|
||||||
|
|
||||||
|
json KinematicsWebAPI::createUnknownCommandResponse(const std::string &req_cmd, const json &req_param) const
|
||||||
|
{
|
||||||
|
return {
|
||||||
|
{"success", false},
|
||||||
|
{"error", "Unknown command: " + req_cmd},
|
||||||
|
{"received_params", req_param},
|
||||||
|
{"timestamp", std::time(nullptr)}};
|
||||||
|
}
|
||||||
|
|
||||||
|
void KinematicsWebAPI::log(const std::string &message)
|
||||||
|
{
|
||||||
|
std::cout << "[" << getCurrentTimestamp() << "] " << message << std::endl;
|
||||||
|
|
||||||
|
if (onLog)
|
||||||
|
{
|
||||||
|
onLog("[" + getCurrentTimestamp() + "] " + message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string KinematicsWebAPI::getCurrentTimestamp()
|
||||||
|
{
|
||||||
|
return utils::get_current_timestamp();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool KinematicsWebAPI::is_running() const
|
||||||
|
{
|
||||||
|
return running_;
|
||||||
|
}
|
||||||
114
src/api/KinematicsWebAPI.FourBarCommands.cpp
Normal file
114
src/api/KinematicsWebAPI.FourBarCommands.cpp
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
#include "KinematicsWebAPI.h"
|
||||||
|
#include "FourBarMechanism/CrankSliderMechanism.h"
|
||||||
|
|
||||||
|
json KinematicsWebAPI::handleFourBarCommand(const std::string &req_cmd, const json &req_param)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
log("Handling " + req_cmd + " command");
|
||||||
|
|
||||||
|
double L_AB = req_param.value("L_AB", 0.5);
|
||||||
|
double L_BS = req_param.value("L_BS", 2.0);
|
||||||
|
double S_OFS = req_param.value("S_OFS", 0.0);
|
||||||
|
double angleDeg = req_param.value("angleDeg", 0.0);
|
||||||
|
|
||||||
|
log("Parameters: L_AB=" + std::to_string(L_AB) +
|
||||||
|
", L_BS=" + std::to_string(L_BS) +
|
||||||
|
", S_OFS=" + std::to_string(S_OFS) +
|
||||||
|
", angleDeg=" + std::to_string(angleDeg));
|
||||||
|
|
||||||
|
std::unique_ptr<CrankSliderMechanism> mechanism(createCrankSliderMechanism());
|
||||||
|
mechanism->setL_AB(L_AB);
|
||||||
|
mechanism->setL_BS(L_BS);
|
||||||
|
mechanism->setS_OFS(S_OFS);
|
||||||
|
|
||||||
|
ValidationResult validation = mechanism->validateParameters();
|
||||||
|
if (!validation.isValid())
|
||||||
|
{
|
||||||
|
return {
|
||||||
|
{"success", false},
|
||||||
|
{"error", "Invalid parameters"},
|
||||||
|
{"validation_errors", validation.Errors},
|
||||||
|
{"validation_warnings", validation.Warnings}};
|
||||||
|
}
|
||||||
|
|
||||||
|
MechanismState state = mechanism->calculate(angleDeg);
|
||||||
|
if (state.hasError())
|
||||||
|
{
|
||||||
|
return {
|
||||||
|
{"success", false},
|
||||||
|
{"error", state.ErrorMessage}};
|
||||||
|
}
|
||||||
|
|
||||||
|
json result;
|
||||||
|
result["success"] = true;
|
||||||
|
|
||||||
|
json points_json;
|
||||||
|
for (const auto &point_pair : state.Points)
|
||||||
|
{
|
||||||
|
points_json[point_pair.first] = {
|
||||||
|
{"x", point_pair.second.X},
|
||||||
|
{"y", point_pair.second.Y}};
|
||||||
|
}
|
||||||
|
result["points"] = points_json;
|
||||||
|
|
||||||
|
json poses_json;
|
||||||
|
for (const auto &pose_pair : state.Poses)
|
||||||
|
{
|
||||||
|
poses_json[pose_pair.first] = {
|
||||||
|
{"tx", pose_pair.second.tx},
|
||||||
|
{"ty", pose_pair.second.ty},
|
||||||
|
{"tz", pose_pair.second.tz},
|
||||||
|
{"qx", pose_pair.second.qx},
|
||||||
|
{"qy", pose_pair.second.qy},
|
||||||
|
{"qz", pose_pair.second.qz},
|
||||||
|
{"qw", pose_pair.second.qw}};
|
||||||
|
}
|
||||||
|
result["poses"] = poses_json;
|
||||||
|
|
||||||
|
json angles_json;
|
||||||
|
for (const auto &angle_pair : state.Angles)
|
||||||
|
{
|
||||||
|
angles_json[angle_pair.first] = angle_pair.second;
|
||||||
|
}
|
||||||
|
result["angles"] = angles_json;
|
||||||
|
|
||||||
|
result["input_value"] = state.InputValue;
|
||||||
|
if (state.hasWarning())
|
||||||
|
{
|
||||||
|
result["warning"] = state.WarningMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
json trajectory_json = json::array();
|
||||||
|
for (const auto &point : mechanism->getTrajectoryPoints())
|
||||||
|
{
|
||||||
|
trajectory_json.push_back({
|
||||||
|
{"x", point.X},
|
||||||
|
{"y", point.Y}});
|
||||||
|
}
|
||||||
|
result["trajectory"] = trajectory_json;
|
||||||
|
|
||||||
|
json slider_trajectory_json = json::array();
|
||||||
|
for (const auto &point : mechanism->getSliderTrajectory())
|
||||||
|
{
|
||||||
|
slider_trajectory_json.push_back({
|
||||||
|
{"x", point.X},
|
||||||
|
{"y", point.Y}});
|
||||||
|
}
|
||||||
|
result["slider_trajectory"] = slider_trajectory_json;
|
||||||
|
|
||||||
|
result["parameters"] = {
|
||||||
|
{"L_AB", L_AB},
|
||||||
|
{"L_BS", L_BS},
|
||||||
|
{"S_OFS", S_OFS},
|
||||||
|
{"angleDeg", angleDeg}};
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
catch (const std::exception &e)
|
||||||
|
{
|
||||||
|
return {
|
||||||
|
{"success", false},
|
||||||
|
{"error", "Failed to Cmd_FourBar_CrankSlider: " + std::string(e.what())}};
|
||||||
|
}
|
||||||
|
}
|
||||||
21
src/api/KinematicsWebAPI.QuadrupedCommands.cpp
Normal file
21
src/api/KinematicsWebAPI.QuadrupedCommands.cpp
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
#include "KinematicsWebAPI.h"
|
||||||
|
#include "QuadrupedRobotSimulation/KinematicsHelper.h"
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
json KinematicsWebAPI::handleQuadrupedCommand(const std::string &req_cmd, const json &req_param)
|
||||||
|
{
|
||||||
|
if (req_cmd == "Cmd_QuadrupedRobot_CalculateAllPointsFromMotorAngles")
|
||||||
|
{
|
||||||
|
std::cout << "[DEBUG] Cmd_QuadrupedRobot_CalculateAllPointsFromMotorAngles: 开始计算点位" << std::endl;
|
||||||
|
return KinematicsHelper::QuadrupedRobot_CalculateAllPointsFromMotorAngles(req_param.dump());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (req_cmd == "Cmd_QuadrupedRobot_PerformForwardKinematics")
|
||||||
|
{
|
||||||
|
std::cout << "[DEBUG] Cmd_QuadrupedRobot_PerformForwardKinematics: 开始执行正运动学" << std::endl;
|
||||||
|
return KinematicsHelper::QuadrupedRobot_PerformForwardKinematics(req_param.dump());
|
||||||
|
}
|
||||||
|
|
||||||
|
return createUnknownCommandResponse(req_cmd, req_param);
|
||||||
|
}
|
||||||
316
src/api/KinematicsWebAPI.RobotCommands.cpp
Normal file
316
src/api/KinematicsWebAPI.RobotCommands.cpp
Normal file
@@ -0,0 +1,316 @@
|
|||||||
|
#include "KinematicsWebAPI.h"
|
||||||
|
|
||||||
|
json KinematicsWebAPI::handleRobotCommand(const std::string &req_cmd, const json &req_param)
|
||||||
|
{
|
||||||
|
json res_data;
|
||||||
|
|
||||||
|
if (req_cmd == "Cmd_Kinematics_inverse_pose_str")
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
log("Handling Cmd_Kinematics_inverse_pose_str command");
|
||||||
|
|
||||||
|
std::string pose_str = req_param.value("pose_str", "");
|
||||||
|
std::string q_init_str = req_param.value("q_init_str", "0,0,0,0,0,0");
|
||||||
|
std::string robot_uuid = req_param.value("robot_uuid", "default");
|
||||||
|
|
||||||
|
auto robot = RobotManager::getRobot(robot_uuid);
|
||||||
|
if (!robot || !robot->isInitialized())
|
||||||
|
{
|
||||||
|
res_data = {{"error", "Robot not found or not initialized"}};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
json result = robot->inversePoseStr(pose_str, q_init_str);
|
||||||
|
if (result.empty())
|
||||||
|
{
|
||||||
|
res_data = {{"error", "Inverse kinematics calculation failed"}};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
res_data = {{"joints", result}, {"success", true}};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (const std::exception &e)
|
||||||
|
{
|
||||||
|
res_data = {{"error", "Failed to calculate inverse kinematics: " + std::string(e.what())}};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (req_cmd == "Cmd_Kinematics_inverse_pose_str_2PSteps")
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
log("Handling Cmd_Kinematics_inverse_pose_str_2PSteps command");
|
||||||
|
|
||||||
|
std::string pose_str = req_param.value("pose_str", "");
|
||||||
|
std::string q_init_str = req_param.value("q_init_str", "0,0,0,0,0,0");
|
||||||
|
std::string robot_uuid = req_param.value("robot_uuid", "default");
|
||||||
|
std::string steps_str = req_param.value("steps_str", "default");
|
||||||
|
int steps = std::stoi(steps_str);
|
||||||
|
|
||||||
|
auto robot = RobotManager::getRobot(robot_uuid);
|
||||||
|
if (!robot || !robot->isInitialized())
|
||||||
|
{
|
||||||
|
res_data = {{"error", "Robot not found or not initialized"}};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
json result = robot->inversePoseStr2PSteps(pose_str, q_init_str, steps);
|
||||||
|
if (result.empty())
|
||||||
|
{
|
||||||
|
res_data = {{"error", "Inverse kinematics calculation failed"}};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
res_data = {{"joints", result}, {"success", true}};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (const std::exception &e)
|
||||||
|
{
|
||||||
|
res_data = {{"error", "Failed to calculate inverse kinematics: " + std::string(e.what())}};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (req_cmd == "Cmd_Kinematics_inverse_pose_str_NoDifference")
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
log("Handling Cmd_Kinematics_inverse_pose_str_NoDifference command");
|
||||||
|
|
||||||
|
std::string pose_str = req_param.value("pose_str", "");
|
||||||
|
std::string q_init_str = req_param.value("q_init_str", "0,0,0,0,0,0");
|
||||||
|
std::string robot_uuid = req_param.value("robot_uuid", "default");
|
||||||
|
|
||||||
|
auto robot = RobotManager::getRobot(robot_uuid);
|
||||||
|
if (!robot || !robot->isInitialized())
|
||||||
|
{
|
||||||
|
res_data = {{"error", "Robot not found or not initialized"}};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
json result = robot->inversePoseStrNoDifference(pose_str, q_init_str);
|
||||||
|
if (result.empty())
|
||||||
|
{
|
||||||
|
res_data = {{"error", "Inverse kinematics calculation failed"}};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
res_data = {{"joints", result}, {"success", true}};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (const std::exception &e)
|
||||||
|
{
|
||||||
|
res_data = {{"error", "Failed to calculate inverse kinematics: " + std::string(e.what())}};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (req_cmd == "Cmd_Kinematics_forward_pose_str")
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
log("Handling Cmd_Kinematics_forward_pose_str command");
|
||||||
|
|
||||||
|
std::string joints_str = req_param.value("q_init_str", "0,0,0,0,0,0");
|
||||||
|
std::string robot_uuid = req_param.value("robot_uuid", "default");
|
||||||
|
|
||||||
|
auto robot = RobotManager::getRobot(robot_uuid);
|
||||||
|
if (!robot || !robot->isInitialized())
|
||||||
|
{
|
||||||
|
res_data = {{"error", "Robot not found or not initialized"}};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
auto joints = robot->parseJointString(joints_str);
|
||||||
|
if (joints.size() != 6)
|
||||||
|
{
|
||||||
|
res_data = {{"error", "Invalid joints format"}};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
double tcp_pose[7];
|
||||||
|
if (robot->calculateFK_TCP(joints.data(), tcp_pose))
|
||||||
|
{
|
||||||
|
res_data = {
|
||||||
|
{"position", {tcp_pose[0], tcp_pose[1], tcp_pose[2]}},
|
||||||
|
{"orientation", {tcp_pose[3], tcp_pose[4], tcp_pose[5], tcp_pose[6]}},
|
||||||
|
{"joints", joints},
|
||||||
|
{"success", true}};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
res_data = {{"error", "Forward kinematics calculation failed"}};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (const std::exception &e)
|
||||||
|
{
|
||||||
|
res_data = {{"error", "Failed to calculate forward kinematics: " + std::string(e.what())}};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (req_cmd == "Cmd_Kinematics_forward_all_joints")
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
log("Handling Cmd_Kinematics_forward_all_joints command");
|
||||||
|
|
||||||
|
std::string joints_str = req_param.value("joints_str", "0,0,0,0,0,0");
|
||||||
|
std::string robot_uuid = req_param.value("robot_uuid", "default");
|
||||||
|
|
||||||
|
auto robot = RobotManager::getRobot(robot_uuid);
|
||||||
|
if (!robot || !robot->isInitialized())
|
||||||
|
{
|
||||||
|
res_data = {{"error", "Robot not found or not initialized"}};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
res_data = {
|
||||||
|
{"OPERATION", robot->handleKinematicsForwardAllJoints(joints_str)},
|
||||||
|
{"success", true}};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (const std::exception &e)
|
||||||
|
{
|
||||||
|
res_data = {{"error", "Failed to calculate forward kinematics for all joints: " + std::string(e.what())}};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (req_cmd == "Cmd_InitRobot")
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
log("Handling Cmd_InitRobot command");
|
||||||
|
|
||||||
|
std::string urdf_base64 = req_param.value("urdf_base64", "");
|
||||||
|
std::string uuid = req_param.value("robot_uuid", "");
|
||||||
|
bool force_update = req_param.value("force_update", true);
|
||||||
|
std::string urdf_content = utils::base64_to_urdf(urdf_base64);
|
||||||
|
|
||||||
|
if (!utils::validate_urdf_base64(urdf_base64))
|
||||||
|
{
|
||||||
|
res_data = {
|
||||||
|
{"success", false},
|
||||||
|
{"message", "Invalid URDF format"},
|
||||||
|
{"timestamp", utils::get_current_time()}};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
auto result = RobotManager::initRobot(urdf_content, uuid, force_update);
|
||||||
|
res_data = {
|
||||||
|
{"success", result.first},
|
||||||
|
{"message", result.second},
|
||||||
|
{"timestamp", utils::get_current_time()}};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (const std::exception &e)
|
||||||
|
{
|
||||||
|
res_data = {{"error", "Failed to initialize robot: " + std::string(e.what())}};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (req_cmd == "Cmd_GetRobot")
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
log("Handling Cmd_GetRobot command");
|
||||||
|
|
||||||
|
std::string uuid = req_param.value("uuid", "");
|
||||||
|
auto robot = RobotManager::getRobot(uuid);
|
||||||
|
|
||||||
|
if (!robot)
|
||||||
|
{
|
||||||
|
res_data = {{"error", "Robot not found"}};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
res_data = {
|
||||||
|
{"success", true},
|
||||||
|
{"uuid", uuid},
|
||||||
|
{"initialized", robot->isInitialized()},
|
||||||
|
{"joints_count", robot->getNumberOfJoints()},
|
||||||
|
{"timestamp", utils::get_current_time()}};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (const std::exception &e)
|
||||||
|
{
|
||||||
|
res_data = {{"error", "Failed to get robot: " + std::string(e.what())}};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (req_cmd == "Cmd_RemoveRobot")
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
log("Handling Cmd_RemoveRobot command");
|
||||||
|
|
||||||
|
std::string uuid = req_param.value("uuid", "");
|
||||||
|
auto result = RobotManager::removeRobot(uuid);
|
||||||
|
|
||||||
|
res_data = {
|
||||||
|
{"success", result.first},
|
||||||
|
{"message", result.second},
|
||||||
|
{"timestamp", utils::get_current_time()}};
|
||||||
|
}
|
||||||
|
catch (const std::exception &e)
|
||||||
|
{
|
||||||
|
res_data = {{"error", "Failed to remove robot: " + std::string(e.what())}};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (req_cmd == "Cmd_ListRobots")
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
log("Handling Cmd_ListRobots command");
|
||||||
|
|
||||||
|
bool detail = req_param.value("detail", false);
|
||||||
|
auto robots = RobotManager::listRobots(detail);
|
||||||
|
|
||||||
|
res_data = {
|
||||||
|
{"success", true},
|
||||||
|
{"robots", robots},
|
||||||
|
{"count", robots.size()},
|
||||||
|
{"timestamp", utils::get_current_time()}};
|
||||||
|
}
|
||||||
|
catch (const std::exception &e)
|
||||||
|
{
|
||||||
|
res_data = {{"error", "Failed to list robots: " + std::string(e.what())}};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (req_cmd == "Cmd_SelectCraftTree")
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
log("Handling Cmd_SelectCraftTree command");
|
||||||
|
std::string tree_id = req_param.value("tree_id", "");
|
||||||
|
res_data = {
|
||||||
|
{"success", true},
|
||||||
|
{"tree_id", tree_id},
|
||||||
|
{"message", "Craft tree selected successfully"},
|
||||||
|
{"timestamp", utils::get_current_time()}};
|
||||||
|
}
|
||||||
|
catch (const std::exception &e)
|
||||||
|
{
|
||||||
|
res_data = {{"error", "Failed to select craft tree: " + std::string(e.what())}};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (req_cmd == "Cmd_AddOperationTree")
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
log("Handling Cmd_AddOperationTree command");
|
||||||
|
std::string tree_name = req_param.value("name", "");
|
||||||
|
json operations = req_param.value("operations", json::array());
|
||||||
|
|
||||||
|
res_data = {
|
||||||
|
{"success", true},
|
||||||
|
{"tree_name", tree_name},
|
||||||
|
{"operations_count", operations.size()},
|
||||||
|
{"message", "Operation tree added successfully"},
|
||||||
|
{"timestamp", utils::get_current_time()}};
|
||||||
|
}
|
||||||
|
catch (const std::exception &e)
|
||||||
|
{
|
||||||
|
res_data = {{"error", "Failed to add operation tree: " + std::string(e.what())}};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return res_data;
|
||||||
|
}
|
||||||
14
src/api/KinematicsWebAPI.SpcCommands.cpp
Normal file
14
src/api/KinematicsWebAPI.SpcCommands.cpp
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
#include "KinematicsWebAPI.h"
|
||||||
|
|
||||||
|
json KinematicsWebAPI::handleSpcCommand(const std::string &req_cmd, const json &req_param)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
log("Handling " + req_cmd + " command");
|
||||||
|
return SpcCalculator::Spc(req_param);
|
||||||
|
}
|
||||||
|
catch (const std::exception &e)
|
||||||
|
{
|
||||||
|
return {{"error", "Failed to Cmd_Spc: " + std::string(e.what())}};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,8 +8,6 @@
|
|||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
|
||||||
#include <stdexcept>
|
|
||||||
|
|
||||||
// 实现 splitByDelimiter 方法
|
// 实现 splitByDelimiter 方法
|
||||||
std::pair<std::string, std::string> StringUtils::splitByDelimiter(
|
std::pair<std::string, std::string> StringUtils::splitByDelimiter(
|
||||||
const std::string &str,
|
const std::string &str,
|
||||||
@@ -88,13 +86,13 @@ std::pair<std::string, std::string> StringUtils::splitStrict(
|
|||||||
namespace utils
|
namespace utils
|
||||||
{
|
{
|
||||||
|
|
||||||
// Base64<EFBFBD>ַ<EFBFBD><EFBFBD><EFBFBD>
|
// Base64 字符表
|
||||||
const std::string base64_chars =
|
const std::string base64_chars =
|
||||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||||
"abcdefghijklmnopqrstuvwxyz"
|
"abcdefghijklmnopqrstuvwxyz"
|
||||||
"0123456789+/";
|
"0123456789+/";
|
||||||
|
|
||||||
// <EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ƿ<EFBFBD>Ϊ<EFBFBD><EFBFBD>Ч<EFBFBD><EFBFBD>base64<EFBFBD>ַ<EFBFBD>
|
// 判断字符是否为有效的 Base64 字符
|
||||||
static bool is_base64(unsigned char c)
|
static bool is_base64(unsigned char c)
|
||||||
{
|
{
|
||||||
return (isalnum(c) || (c == '+') || (c == '/'));
|
return (isalnum(c) || (c == '+') || (c == '/'));
|
||||||
@@ -134,7 +132,7 @@ namespace utils
|
|||||||
// res.set_header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS, PATCH");
|
// res.set_header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS, PATCH");
|
||||||
// res.set_header("Access-Control-Allow-Headers", "Content-Type, Authorization, X-Requested-With, X-API-Key, Accept, Origin");
|
// res.set_header("Access-Control-Allow-Headers", "Content-Type, Authorization, X-Requested-With, X-API-Key, Accept, Origin");
|
||||||
// res.set_header("Access-Control-Expose-Headers", "Content-Length, Content-Type, X-Request-Id");
|
// res.set_header("Access-Control-Expose-Headers", "Content-Length, Content-Type, X-Request-Id");
|
||||||
// res.set_header("Access-Control-Max-Age", "86400"); // 24Сʱ
|
// res.set_header("Access-Control-Max-Age", "86400"); // 24 小时
|
||||||
// res.set_header("Vary", "Origin");
|
// res.set_header("Vary", "Origin");
|
||||||
// }
|
// }
|
||||||
|
|
||||||
@@ -171,11 +169,11 @@ namespace utils
|
|||||||
unsigned char c = str[i];
|
unsigned char c = str[i];
|
||||||
if (c <= 0x7F)
|
if (c <= 0x7F)
|
||||||
{
|
{
|
||||||
continue; // ASCII<EFBFBD>ַ<EFBFBD>
|
continue; // ASCII 字符
|
||||||
}
|
}
|
||||||
else if ((c & 0xE0) == 0xC0)
|
else if ((c & 0xE0) == 0xC0)
|
||||||
{
|
{
|
||||||
// 2<EFBFBD>ֽ<EFBFBD>UTF-8
|
// 2 字节 UTF-8
|
||||||
if (i + 1 >= str.size() || (str[i + 1] & 0xC0) != 0x80)
|
if (i + 1 >= str.size() || (str[i + 1] & 0xC0) != 0x80)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
@@ -184,7 +182,7 @@ namespace utils
|
|||||||
}
|
}
|
||||||
else if ((c & 0xF0) == 0xE0)
|
else if ((c & 0xF0) == 0xE0)
|
||||||
{
|
{
|
||||||
// 3<EFBFBD>ֽ<EFBFBD>UTF-8
|
// 3 字节 UTF-8
|
||||||
if (i + 2 >= str.size() || (str[i + 1] & 0xC0) != 0x80 || (str[i + 2] & 0xC0) != 0x80)
|
if (i + 2 >= str.size() || (str[i + 1] & 0xC0) != 0x80 || (str[i + 2] & 0xC0) != 0x80)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
@@ -193,7 +191,7 @@ namespace utils
|
|||||||
}
|
}
|
||||||
else if ((c & 0xF8) == 0xF0)
|
else if ((c & 0xF8) == 0xF0)
|
||||||
{
|
{
|
||||||
// 4<EFBFBD>ֽ<EFBFBD>UTF-8
|
// 4 字节 UTF-8
|
||||||
if (i + 3 >= str.size() || (str[i + 1] & 0xC0) != 0x80 ||
|
if (i + 3 >= str.size() || (str[i + 1] & 0xC0) != 0x80 ||
|
||||||
(str[i + 2] & 0xC0) != 0x80 || (str[i + 3] & 0xC0) != 0x80)
|
(str[i + 2] & 0xC0) != 0x80 || (str[i + 3] & 0xC0) != 0x80)
|
||||||
{
|
{
|
||||||
@@ -203,7 +201,7 @@ namespace utils
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return false; // <EFBFBD><EFBFBD>Ч<EFBFBD><EFBFBD>UTF-8<EFBFBD>ֽ<EFBFBD>
|
return false; // 非法 UTF-8 字节
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@@ -219,11 +217,11 @@ namespace utils
|
|||||||
unsigned char c = str[i];
|
unsigned char c = str[i];
|
||||||
if (c <= 0x7F)
|
if (c <= 0x7F)
|
||||||
{
|
{
|
||||||
result += c; // ASCII<EFBFBD>ַ<EFBFBD>
|
result += c; // ASCII 字符
|
||||||
}
|
}
|
||||||
else if ((c & 0xE0) == 0xC0)
|
else if ((c & 0xE0) == 0xC0)
|
||||||
{
|
{
|
||||||
// 2<EFBFBD>ֽ<EFBFBD>UTF-8
|
// 2 字节 UTF-8
|
||||||
if (i + 1 < str.size() && (str[i + 1] & 0xC0) == 0x80)
|
if (i + 1 < str.size() && (str[i + 1] & 0xC0) == 0x80)
|
||||||
{
|
{
|
||||||
result += c;
|
result += c;
|
||||||
@@ -233,7 +231,7 @@ namespace utils
|
|||||||
}
|
}
|
||||||
else if ((c & 0xF0) == 0xE0)
|
else if ((c & 0xF0) == 0xE0)
|
||||||
{
|
{
|
||||||
// 3<EFBFBD>ֽ<EFBFBD>UTF-8
|
// 3 字节 UTF-8
|
||||||
if (i + 2 < str.size() && (str[i + 1] & 0xC0) == 0x80 && (str[i + 2] & 0xC0) == 0x80)
|
if (i + 2 < str.size() && (str[i + 1] & 0xC0) == 0x80 && (str[i + 2] & 0xC0) == 0x80)
|
||||||
{
|
{
|
||||||
result += c;
|
result += c;
|
||||||
@@ -244,7 +242,7 @@ namespace utils
|
|||||||
}
|
}
|
||||||
else if ((c & 0xF8) == 0xF0)
|
else if ((c & 0xF8) == 0xF0)
|
||||||
{
|
{
|
||||||
// 4<EFBFBD>ֽ<EFBFBD>UTF-8
|
// 4 字节 UTF-8
|
||||||
if (i + 3 < str.size() && (str[i + 1] & 0xC0) == 0x80 &&
|
if (i + 3 < str.size() && (str[i + 1] & 0xC0) == 0x80 &&
|
||||||
(str[i + 2] & 0xC0) == 0x80 && (str[i + 3] & 0xC0) == 0x80)
|
(str[i + 2] & 0xC0) == 0x80 && (str[i + 3] & 0xC0) == 0x80)
|
||||||
{
|
{
|
||||||
@@ -255,7 +253,7 @@ namespace utils
|
|||||||
i += 3;
|
i += 3;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ч<EFBFBD><EFBFBD>UTF-8<EFBFBD>ֽ<EFBFBD>
|
// 跳过非法 UTF-8 字节
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -285,7 +283,7 @@ namespace utils
|
|||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Base64<EFBFBD><EFBFBD><EFBFBD>뺯<EFBFBD><EFBFBD>
|
// Base64 解码函数
|
||||||
std::string base64_decode(const std::string &encoded_string)
|
std::string base64_decode(const std::string &encoded_string)
|
||||||
{
|
{
|
||||||
int in_len = encoded_string.size();
|
int in_len = encoded_string.size();
|
||||||
@@ -295,7 +293,7 @@ namespace utils
|
|||||||
unsigned char char_array_4[4], char_array_3[3];
|
unsigned char char_array_4[4], char_array_3[3];
|
||||||
std::string ret;
|
std::string ret;
|
||||||
|
|
||||||
// <EFBFBD>Ƴ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ܵĻ<EFBFBD><EFBFBD>з<EFBFBD><EFBFBD>Ϳո<EFBFBD>
|
// 去掉输入中的换行和空格
|
||||||
std::string clean_encoded;
|
std::string clean_encoded;
|
||||||
for (char c : encoded_string)
|
for (char c : encoded_string)
|
||||||
{
|
{
|
||||||
@@ -346,7 +344,7 @@ namespace utils
|
|||||||
size_t pos = base64_chars.find(char_array_4[j]);
|
size_t pos = base64_chars.find(char_array_4[j]);
|
||||||
if (pos == std::string::npos && j >= i)
|
if (pos == std::string::npos && j >= i)
|
||||||
{
|
{
|
||||||
// <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ַ<EFBFBD>λ<EFBFBD><EFBFBD>Ϊ0
|
// 补齐的占位字符按 0 处理
|
||||||
char_array_4[j] = 0;
|
char_array_4[j] = 0;
|
||||||
}
|
}
|
||||||
else if (pos != std::string::npos)
|
else if (pos != std::string::npos)
|
||||||
@@ -372,14 +370,14 @@ namespace utils
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
// <EFBFBD><EFBFBD>base64<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>URDFת<EFBFBD><EFBFBD>Ϊ<EFBFBD>ַ<EFBFBD><EFBFBD><EFBFBD>
|
// 将 Base64 编码的 URDF 转成字符串
|
||||||
std::string base64_to_urdf(const std::string &base64_urdf)
|
std::string base64_to_urdf(const std::string &base64_urdf)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
std::string urdf_content = base64_decode(base64_urdf);
|
std::string urdf_content = base64_decode(base64_urdf);
|
||||||
|
|
||||||
// <EFBFBD><EFBFBD>֤<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ƿ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ч<EFBFBD><EFBFBD>URDF/XML
|
// 校验解码结果是否像合法的 URDF/XML
|
||||||
if (urdf_content.find("<?xml") != std::string::npos ||
|
if (urdf_content.find("<?xml") != std::string::npos ||
|
||||||
urdf_content.find("<robot") != std::string::npos)
|
urdf_content.find("<robot") != std::string::npos)
|
||||||
{
|
{
|
||||||
@@ -396,7 +394,7 @@ namespace utils
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// <EFBFBD><EFBFBD>base64<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>URDF<EFBFBD><EFBFBD><EFBFBD>浽<EFBFBD>ļ<EFBFBD>
|
// 将 Base64 编码的 URDF 保存到文件
|
||||||
bool save_base64_urdf_to_file(const std::string &base64_urdf, const std::string &filename)
|
bool save_base64_urdf_to_file(const std::string &base64_urdf, const std::string &filename)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@@ -411,7 +409,7 @@ namespace utils
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// <EFBFBD><EFBFBD>URDF<EFBFBD>ַ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>浽<EFBFBD>ļ<EFBFBD>
|
// 将 URDF 字符串保存到文件
|
||||||
bool save_urdf_string_to_file(const std::string &urdf_content, const std::string &filename)
|
bool save_urdf_string_to_file(const std::string &urdf_content, const std::string &filename)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@@ -437,14 +435,14 @@ namespace utils
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// <EFBFBD><EFBFBD>֤base64<EFBFBD>ַ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ƿ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ч<EFBFBD><EFBFBD>URDF
|
// 校验 Base64 字符串是否可解析为合法 URDF
|
||||||
bool validate_urdf_base64(const std::string &base64_urdf)
|
bool validate_urdf_base64(const std::string &base64_urdf)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
std::string urdf_content = base64_to_urdf(base64_urdf);
|
std::string urdf_content = base64_to_urdf(base64_urdf);
|
||||||
|
|
||||||
// <EFBFBD><EFBFBD>URDF<EFBFBD><EFBFBD>֤
|
// 基础 URDF 结构校验
|
||||||
bool has_xml_decl = urdf_content.find("<?xml") != std::string::npos;
|
bool has_xml_decl = urdf_content.find("<?xml") != std::string::npos;
|
||||||
bool has_robot_tag = urdf_content.find("<robot") != std::string::npos;
|
bool has_robot_tag = urdf_content.find("<robot") != std::string::npos;
|
||||||
bool has_link_tag = urdf_content.find("<link") != std::string::npos;
|
bool has_link_tag = urdf_content.find("<link") != std::string::npos;
|
||||||
|
|||||||
Reference in New Issue
Block a user