From 9b14dec67c9ed5a4210e6be668895d67c032ef06 Mon Sep 17 00:00:00 2001 From: zhangshun <453905631@qq.com> Date: Mon, 1 Jun 2026 16:24:38 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E6=9E=84=E5=BB=BA=E8=84=9A?= =?UTF-8?q?=E6=9C=AC=E5=92=8CWASM=E6=B5=8B=E8=AF=95=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- scripts/build.ps1 | 104 +++++++++++++++------ scripts/run_wasm_tests.js | 14 ++- src/KinematicsWebAPI.cpp | 192 +++++++++++++++++++------------------- src/Robot.cpp | Bin 90904 -> 48554 bytes 4 files changed, 182 insertions(+), 128 deletions(-) diff --git a/scripts/build.ps1 b/scripts/build.ps1 index 883b360..cf130dc 100644 --- a/scripts/build.ps1 +++ b/scripts/build.ps1 @@ -1,4 +1,4 @@ - + # build.ps1 - 智能 WASM 构建脚本 param( [switch]$Clean, @@ -13,16 +13,50 @@ Write-Host "=========================================" -ForegroundColor Cyan Write-Host "" # 1. 设置环境变量 -$EmsdkPath = "E:\emsdk" -if (-not (Test-Path $EmsdkPath)) { +$emsdkCandidates = @() + +if ($env:EMSDK) { + $emsdkCandidates += $env:EMSDK +} + +$emsdkCandidates += "E:\emsdk" +$emsdkCandidates += (Join-Path (Split-Path $PSScriptRoot -Parent) "..\emsdk") +$emsdkCandidates += (Join-Path (Split-Path $PSScriptRoot -Parent) "emsdk") + +$emccCommand = Get-Command emcc -ErrorAction SilentlyContinue +if ($emccCommand -and $emccCommand.Source) { + $emccSourceDir = Split-Path $emccCommand.Source -Parent + if (Test-Path (Join-Path $emccSourceDir "emcc.ps1")) { + $emsdkCandidates += (Split-Path (Split-Path $emccSourceDir -Parent) -Parent) + } +} + +$EmsdkPath = $null +foreach ($candidate in $emsdkCandidates | Select-Object -Unique) { + if (-not [string]::IsNullOrWhiteSpace($candidate)) { + try { + if (Test-Path $candidate) { + $resolvedCandidate = [System.IO.Path]::GetFullPath($candidate) + if (Test-Path (Join-Path $resolvedCandidate "upstream\emscripten\emcc.ps1")) { + $EmsdkPath = $resolvedCandidate + break + } + } + } catch { + continue + } + } +} + +if (-not $EmsdkPath) { Write-Host "[错误] 未找到 Emscripten" -ForegroundColor Red - Write-Host "请确保 Emscripten 安装在: $EmsdkPath" -ForegroundColor Yellow + Write-Host "请设置 EMSDK 环境变量,或确认 emsdk 已安装。" -ForegroundColor Yellow exit 1 } # 设置 PATH $env:PATH = "$EmsdkPath\upstream\emscripten;$env:PATH" -Write-Host "[✓] 设置 Emscripten 环境" -ForegroundColor Green +Write-Host "[✓] 设置 Emscripten 环境: $EmsdkPath" -ForegroundColor Green # 2. 清理旧构建 if ($Clean) { @@ -84,8 +118,8 @@ $compileResult = emcc ` -lorocos-kdl ` -std=c++17 ` -s WASM=1 ` - -s EXPORTED_FUNCTIONS='["_init_func","_func","_smart_process_json","_smart_get_function_list","_smart_get_function_info","_smart_test_match","_smart_get_version","_smart_free_string","_wasm_malloc","_wasm_free","_malloc","_free","_add","_subtract","_multiply","_divide","_fibonacci","_create_buffer","_destroy_buffer","_compute_sum","_get_greeting","_main"]' ` - -s EXPORTED_RUNTIME_METHODS='["UTF8ToString","stringToUTF8","lengthBytesUTF8","ccall","cwrap","HEAPU8","HEAP8","HEAPF32","HEAPF64","allocateUTF8"]' ` + -s EXPORTED_FUNCTIONS='["_init_func","_func","_smart_process_json","_smart_get_function_list","_smart_get_function_info","_smart_test_match","_smart_get_version","_smart_free_string","_malloc","_free","_add","_subtract","_multiply","_divide","_fibonacci","_create_buffer","_destroy_buffer","_compute_sum","_get_greeting","_main"]' ` + -s EXPORTED_RUNTIME_METHODS='["UTF8ToString","stringToUTF8","lengthBytesUTF8","ccall","cwrap","HEAPU8","HEAP8","HEAPF32","HEAPF64"]' ` -s MODULARIZE=1 ` -s EXPORT_NAME='createSmartMathModule' ` -s ALLOW_MEMORY_GROWTH=1 ` @@ -125,28 +159,28 @@ $wasmSize = $wasmFile.Length / 1KB # 6. 显示构建结果 Write-Host "" -Write-Host "🎉 构建成功!" -ForegroundColor Green -Write-Host ("═" * 50) -ForegroundColor DarkGray -Write-Host "📦 生成文件:" -ForegroundColor White -Write-Host "├─ smart_math.js ($($jsSize.ToString('F1')) KB)" -ForegroundColor Gray -Write-Host "└─ smart_math.wasm ($($wasmSize.ToString('F1')) KB)" -ForegroundColor Gray +Write-Host "构建成功" -ForegroundColor Green +Write-Host ("=" * 50) -ForegroundColor DarkGray +Write-Host "生成文件:" -ForegroundColor White +Write-Host " - smart_math.js ($($jsSize.ToString('F1')) KB)" -ForegroundColor Gray +Write-Host " - smart_math.wasm ($($wasmSize.ToString('F1')) KB)" -ForegroundColor Gray Write-Host "" -Write-Host "✨ 功能特性:" -ForegroundColor White -Write-Host "├─ ✅ 机器人正逆解业务接口 (func/init_func)" -ForegroundColor Gray -Write-Host "├─ ✅ SPC / 四足机器人 / 连杆机构业务接口" -ForegroundColor Gray -Write-Host "├─ ✅ 智能 JSON 数学测试接口" -ForegroundColor Gray -Write-Host "├─ ✅ Node / Web 双环境加载" -ForegroundColor Gray -Write-Host "├─ ✅ 与 Emscripten 内存管理兼容" -ForegroundColor Gray -Write-Host "└─ ✅ 批量回归测试可直接调用" -ForegroundColor Gray +Write-Host "功能特性:" -ForegroundColor White +Write-Host " - 机器人正逆解业务接口 (func/init_func)" -ForegroundColor Gray +Write-Host " - SPC / 四足机器人 / 连杆机构业务接口" -ForegroundColor Gray +Write-Host " - 智能 JSON 数学测试接口" -ForegroundColor Gray +Write-Host " - Node / Web 双环境加载" -ForegroundColor Gray +Write-Host " - 与 Emscripten 内存管理兼容" -ForegroundColor Gray +Write-Host " - 批量回归测试可直接调用" -ForegroundColor Gray Write-Host "" -Write-Host "📁 输出目录: $((Get-Location).Path)\public\wasm" -ForegroundColor Gray -Write-Host "⏱️ 编译耗时: $($duration.ToString('F2')) 秒" -ForegroundColor Gray +Write-Host "输出目录: $((Get-Location).Path)\public\wasm" -ForegroundColor Gray +Write-Host "编译耗时: $($duration.ToString('F2')) 秒" -ForegroundColor Gray Write-Host "" # 7. 生成 JavaScript 包装器(可选) Write-Host "[ ] 生成 JavaScript 包装器..." -ForegroundColor Cyan -$jsWrapper = @" +$jsWrapper = @' // Smart WASM JSON API JavaScript 包装器 class SmartWasmAPI { constructor() { @@ -154,6 +188,13 @@ class SmartWasmAPI { this.initialized = false; } + allocString(value) { + const size = this.module.lengthBytesUTF8(value) + 1; + const ptr = this.module._malloc(size); + this.module.stringToUTF8(value, ptr, size); + return ptr; + } + async initialize() { if (this.initialized) return; @@ -192,7 +233,7 @@ class SmartWasmAPI { let responsePtr = null; try { - requestPtr = this.module.allocateUTF8(JSON.stringify(jsonRequest)); + requestPtr = this.allocString(JSON.stringify(jsonRequest)); responsePtr = this.module._func(requestPtr); const responseStr = this.module.UTF8ToString(responsePtr); return JSON.parse(responseStr); @@ -220,7 +261,7 @@ class SmartWasmAPI { try { // 将 JSON 字符串转换为 WASM 内存中的字符串 - requestPtr = this.module.allocateUTF8(JSON.stringify(jsonRequest)); + requestPtr = this.allocString(JSON.stringify(jsonRequest)); // 调用 WASM 函数 responsePtr = this.module._smart_process_json(requestPtr); @@ -286,7 +327,7 @@ class SmartWasmAPI { let responsePtr = null; try { - requestPtr = this.module.allocateUTF8(JSON.stringify(jsonRequest)); + requestPtr = this.allocString(JSON.stringify(jsonRequest)); responsePtr = this.module._smart_test_match(requestPtr); const responseStr = this.module.UTF8ToString(responsePtr); return JSON.parse(responseStr); @@ -354,7 +395,7 @@ if (typeof window !== 'undefined') { } export default SmartWasmAPI; -"@ +'@ $jsWrapper | Out-File -FilePath "public/wasm/smart_api_wrapper.js" -Encoding UTF8 Write-Host "[✓] 生成 JavaScript 包装器" -ForegroundColor Green @@ -386,9 +427,10 @@ if ($Serve) { } Write-Host "" -Write-Host "🚀 使用提示:" -ForegroundColor White -Write-Host "├─ 在 VS Code 中按 F5 启动调试" -ForegroundColor Gray -Write-Host "├─ 使用 Ctrl+Shift+B 快速构建" -ForegroundColor Gray -Write-Host "├─ 查看 public/wasm/smart_api_wrapper.js 了解 JavaScript API 使用" -ForegroundColor Gray -Write-Host "└─ 运行 \`npm run serve\` 启动开发服务器" -ForegroundColor Gray +Write-Host "使用提示:" -ForegroundColor White +Write-Host " - 在 VS Code 中按 F5 启动调试" -ForegroundColor Gray +Write-Host " - 使用 Ctrl+Shift+B 快速构建" -ForegroundColor Gray +Write-Host " - 查看 public/wasm/smart_api_wrapper.js 了解 JavaScript API 使用" -ForegroundColor Gray +Write-Host " - 运行 npm run serve 启动开发服务器" -ForegroundColor Gray Write-Host "" + diff --git a/scripts/run_wasm_tests.js b/scripts/run_wasm_tests.js index db93d16..7d76f48 100644 --- a/scripts/run_wasm_tests.js +++ b/scripts/run_wasm_tests.js @@ -76,9 +76,16 @@ async function loadModule(modulePath) { throw new Error(`Unsupported WASM loader type: ${typeof required}`); } +function allocString(module, value) { + const size = module.lengthBytesUTF8(value) + 1; + const ptr = module._malloc(size); + module.stringToUTF8(value, ptr, size); + return ptr; +} + function callJsonFunction(module, exportName, payload) { const requestText = typeof payload === "string" ? payload : JSON.stringify(payload); - const requestPtr = module.allocateUTF8(requestText); + const requestPtr = allocString(module, requestText); let responsePtr = 0; try { @@ -343,7 +350,7 @@ const suite = [ assertions: [ { path: "success", equals: true }, { path: "res_data.success", equals: true }, - { path: "res_data.OPERATION.frames", lengthEquals: 2 }, + { path: "res_data.OPERATION.OPERATION.frames", lengthEquals: 2 }, ], }, { @@ -435,7 +442,8 @@ async function main() { ensure(typeof module._init_func === "function", "_init_func is not exported"); ensure(typeof module._func === "function", "_func is not exported"); - ensure(typeof module.allocateUTF8 === "function", "allocateUTF8 is not exported"); + ensure(typeof module.lengthBytesUTF8 === "function", "lengthBytesUTF8 is not exported"); + ensure(typeof module.stringToUTF8 === "function", "stringToUTF8 is not exported"); ensure(typeof module.UTF8ToString === "function", "UTF8ToString is not exported"); const initResponse = callNoArgJsonFunction(module, "_init_func"); diff --git a/src/KinematicsWebAPI.cpp b/src/KinematicsWebAPI.cpp index a49a70f..1ea20fc 100644 --- a/src/KinematicsWebAPI.cpp +++ b/src/KinematicsWebAPI.cpp @@ -426,105 +426,109 @@ std::string KinematicsWebAPI::func(std::string sanitized_body) {"error", "Invalid parameters"}, {"validation_errors", validation.Errors}, {"validation_warnings", validation.Warnings}}; - return res_data; } - - // 执行计算 - MechanismState state = mechanism->calculate(angleDeg); - - // 检查是否有错误 - if (state.hasError()) + else { - res_data = { - {"success", false}, - {"error", state.ErrorMessage}}; - return res_data; + // 执行计算 + 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 trajectory = mechanism->getTrajectoryPoints(); + std::vector 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; + } } - - // 构建响应数据 - 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 trajectory = mechanism->getTrajectoryPoints(); - std::vector 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())}}; + res_data = { + {"success", false}, + {"error", "Failed to Cmd_FourBar_CrankSlider: " + std::string(e.what())}}; } } else if (req_cmd == "Cmd_QuadrupedRobot_CalculateAllPointsFromMotorAngles") @@ -602,4 +606,4 @@ std::string KinematicsWebAPI::getCurrentTimestamp() bool KinematicsWebAPI::is_running() const { return running_; -} \ No newline at end of file +} diff --git a/src/Robot.cpp b/src/Robot.cpp index a6a047d240263be05046d33ebce13ae66e45b81d..d1e72eb97a48836e4d46b2e8835759f613d97341 100644 GIT binary patch literal 48554 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 literal 90904 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