修复构建脚本和WASM测试问题

This commit is contained in:
zhangshun
2026-06-01 16:24:38 +08:00
parent 40f9bdb590
commit 9b14dec67c
4 changed files with 182 additions and 128 deletions

View File

@@ -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 ""

View File

@@ -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");

View File

@@ -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<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;
}
}
// 构建响应数据
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())}};
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_;
}
}

Binary file not shown.