新增四类连杆统一仿真接口
This commit is contained in:
@@ -122,7 +122,8 @@ json KinematicsWebAPI::dispatchCommand(const std::string &req_cmd, const json &r
|
||||
return handleSpcCommand(req_cmd, req_param);
|
||||
}
|
||||
|
||||
if (req_cmd == "Cmd_FourBar_CrankSlider" ||
|
||||
if (req_cmd == "Cmd_FourBar_Simulate" ||
|
||||
req_cmd == "Cmd_FourBar_CrankSlider" ||
|
||||
req_cmd == "Cmd_FourBar_CrankSlider_Simulate")
|
||||
{
|
||||
return handleFourBarCommand(req_cmd, req_param);
|
||||
|
||||
@@ -1,9 +1,45 @@
|
||||
#include "KinematicsWebAPI.h"
|
||||
#include "FourBarMechanism/CrankSliderMechanism.h"
|
||||
#include "FourBarMechanism/CrankRockingBlockMechanism_Forward.h"
|
||||
#include "FourBarMechanism/FourBarMechanism.h"
|
||||
#include "FourBarMechanism/SliderCrankMechanism.h"
|
||||
#include <cmath>
|
||||
#include <memory>
|
||||
|
||||
namespace
|
||||
{
|
||||
// 读取兼容字段,便于新统一接口沿用旧曲柄滑块参数名。
|
||||
double valueAny(const json ¶m, std::initializer_list<const char *> keys, double defaultValue)
|
||||
{
|
||||
for (const char *key : keys)
|
||||
{
|
||||
if (param.contains(key) && param[key].is_number())
|
||||
{
|
||||
return param[key].get<double>();
|
||||
}
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
// 构造统一的业务失败返回,供四类连杆仿真复用。
|
||||
json buildErrorJson(const std::string &error, const json &validationErrors = json::array(), const json &validationWarnings = json::array())
|
||||
{
|
||||
json result = {
|
||||
{"success", false},
|
||||
{"error", error}};
|
||||
|
||||
if (!validationErrors.empty())
|
||||
{
|
||||
result["validation_errors"] = validationErrors;
|
||||
}
|
||||
if (!validationWarnings.empty())
|
||||
{
|
||||
result["validation_warnings"] = validationWarnings;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// 将机构点位转换为接口统一 JSON。
|
||||
json buildPointsJson(const MechanismState &state)
|
||||
{
|
||||
@@ -60,12 +96,14 @@ json buildTrajectoryJson(const std::vector<Vector2D> &points)
|
||||
}
|
||||
|
||||
// 组装单帧机构状态,供实时接口和批量仿真接口复用。
|
||||
json buildFrameJson(const MechanismState &state, int frame, double time, double angleDeg)
|
||||
json buildFrameJson(const MechanismState &state, int frame, double time, double inputValue, const std::string &inputName)
|
||||
{
|
||||
json frame_json;
|
||||
frame_json["frame"] = frame;
|
||||
frame_json["time"] = time;
|
||||
frame_json["angleDeg"] = angleDeg;
|
||||
frame_json[inputName] = inputValue;
|
||||
frame_json["inputName"] = inputName;
|
||||
frame_json["inputValue"] = inputValue;
|
||||
frame_json["input_value"] = state.InputValue;
|
||||
frame_json["points"] = buildPointsJson(state);
|
||||
frame_json["poses"] = buildPosesJson(state);
|
||||
@@ -76,6 +114,260 @@ json buildFrameJson(const MechanismState &state, int frame, double time, double
|
||||
}
|
||||
return frame_json;
|
||||
}
|
||||
|
||||
// 校验仿真时间参数并返回帧数。
|
||||
json validateSimulationTimeline(double duration, double stepTime, int &frameCount)
|
||||
{
|
||||
if (duration <= 0.0)
|
||||
{
|
||||
return buildErrorJson("Invalid simulation parameters", json::array({"duration 必须大于 0"}));
|
||||
}
|
||||
|
||||
if (stepTime <= 0.0 || stepTime > duration)
|
||||
{
|
||||
return buildErrorJson("Invalid simulation parameters", json::array({"stepTime 必须大于 0 且不能大于 duration"}));
|
||||
}
|
||||
|
||||
frameCount = static_cast<int>(std::ceil(duration / stepTime)) + 1;
|
||||
if (frameCount < 2 || frameCount > 1000)
|
||||
{
|
||||
return buildErrorJson("Invalid simulation parameters", json::array({"仿真帧数必须在 2 到 1000 之间"}));
|
||||
}
|
||||
|
||||
return json();
|
||||
}
|
||||
|
||||
// 将 ValidationResult 转换为统一业务错误。
|
||||
json validateOrError(const ValidationResult &validation)
|
||||
{
|
||||
if (validation.isValid())
|
||||
{
|
||||
return json();
|
||||
}
|
||||
|
||||
return buildErrorJson("Invalid parameters", validation.Errors, validation.Warnings);
|
||||
}
|
||||
|
||||
// 按统一格式生成逐帧仿真数据。
|
||||
template <typename CalculateFunc>
|
||||
json buildSimulationFrames(int frameCount, double duration, double stepTime, double startValue, double endValue, const std::string &inputName, CalculateFunc calculate)
|
||||
{
|
||||
json frames_json = json::array();
|
||||
for (int frame = 0; frame < frameCount; ++frame)
|
||||
{
|
||||
const double currentTime = frame == frameCount - 1 ? duration : stepTime * frame;
|
||||
const double progress = duration <= 0.0 ? 0.0 : currentTime / duration;
|
||||
const double currentValue = startValue + (endValue - startValue) * progress;
|
||||
MechanismState state = calculate(currentValue);
|
||||
if (state.hasError())
|
||||
{
|
||||
return {
|
||||
{"success", false},
|
||||
{"error", state.ErrorMessage},
|
||||
{"failed_frame", frame},
|
||||
{"failed_time", currentTime},
|
||||
{"failed_input", currentValue},
|
||||
{"failed_input_name", inputName}};
|
||||
}
|
||||
|
||||
frames_json.push_back(buildFrameJson(state, frame, currentTime, currentValue, inputName));
|
||||
}
|
||||
|
||||
return frames_json;
|
||||
}
|
||||
|
||||
// 统一四连杆仿真接口,覆盖 PDPS 的 PRRR/RPRR/RRRP/RRRR 四类结构。
|
||||
json simulateUnifiedFourBar(const json &req_param)
|
||||
{
|
||||
const std::string mechanismType = req_param.value("mechanismType", "RRRP");
|
||||
const double duration = req_param.value("duration", 2.0);
|
||||
const double stepTime = req_param.value("stepTime", 0.02);
|
||||
int frameCount = 0;
|
||||
|
||||
json timelineError = validateSimulationTimeline(duration, stepTime, frameCount);
|
||||
if (!timelineError.is_null())
|
||||
{
|
||||
return timelineError;
|
||||
}
|
||||
|
||||
if (mechanismType == "RRRP")
|
||||
{
|
||||
const double L_AB = req_param.value("L_AB", 0.5);
|
||||
const double L_BS = req_param.value("L_BS", 2.0);
|
||||
const double S_OFS = req_param.value("S_OFS", 0.0);
|
||||
const double startAngleDeg = valueAny(req_param, {"startAngleDeg", "startValue", "angleDeg"}, 0.0);
|
||||
const double endAngleDeg = valueAny(req_param, {"endAngleDeg", "endValue"}, 360.0);
|
||||
|
||||
std::unique_ptr<CrankSliderMechanism> mechanism(createCrankSliderMechanism());
|
||||
mechanism->setL_AB(L_AB);
|
||||
mechanism->setL_BS(L_BS);
|
||||
mechanism->setS_OFS(S_OFS);
|
||||
|
||||
json validationError = validateOrError(mechanism->validateParameters());
|
||||
if (!validationError.is_null())
|
||||
{
|
||||
return validationError;
|
||||
}
|
||||
|
||||
json frames_json = buildSimulationFrames(frameCount, duration, stepTime, startAngleDeg, endAngleDeg, "angleDeg", [&](double value) {
|
||||
return mechanism->calculate(value);
|
||||
});
|
||||
if (!frames_json.is_array())
|
||||
{
|
||||
return frames_json;
|
||||
}
|
||||
|
||||
return {
|
||||
{"success", true},
|
||||
{"mechanismType", mechanismType},
|
||||
{"inputName", "angleDeg"},
|
||||
{"frames", frames_json},
|
||||
{"frame_count", frameCount},
|
||||
{"duration", duration},
|
||||
{"stepTime", stepTime},
|
||||
{"startValue", startAngleDeg},
|
||||
{"endValue", endAngleDeg},
|
||||
{"startAngleDeg", startAngleDeg},
|
||||
{"endAngleDeg", endAngleDeg},
|
||||
{"trajectory", buildTrajectoryJson(mechanism->getTrajectoryPoints())},
|
||||
{"slider_trajectory", buildTrajectoryJson(mechanism->getSliderTrajectory())},
|
||||
{"parameters", {{"mechanismType", mechanismType}, {"L_AB", L_AB}, {"L_BS", L_BS}, {"S_OFS", S_OFS}, {"startAngleDeg", startAngleDeg}, {"endAngleDeg", endAngleDeg}, {"duration", duration}, {"stepTime", stepTime}}}};
|
||||
}
|
||||
|
||||
if (mechanismType == "PRRR")
|
||||
{
|
||||
const double L_AB = req_param.value("L_AB", 0.5);
|
||||
const double L_BS = req_param.value("L_BS", 2.0);
|
||||
const double S_OFS = req_param.value("S_OFS", 0.0);
|
||||
|
||||
std::unique_ptr<SliderCrankMechanism> mechanism(createSliderCrankMechanism());
|
||||
mechanism->setLink(L_AB, L_BS, S_OFS);
|
||||
|
||||
json validationError = validateOrError(mechanism->validateParameters());
|
||||
if (!validationError.is_null())
|
||||
{
|
||||
return validationError;
|
||||
}
|
||||
|
||||
const auto inputRange = mechanism->getInputRange();
|
||||
const double startSliderX = valueAny(req_param, {"startSliderX", "startValue", "sliderX"}, inputRange.first);
|
||||
const double endSliderX = valueAny(req_param, {"endSliderX", "endValue"}, inputRange.second);
|
||||
json frames_json = buildSimulationFrames(frameCount, duration, stepTime, startSliderX, endSliderX, "sliderX", [&](double value) {
|
||||
return mechanism->calculate(value);
|
||||
});
|
||||
if (!frames_json.is_array())
|
||||
{
|
||||
return frames_json;
|
||||
}
|
||||
|
||||
return {
|
||||
{"success", true},
|
||||
{"mechanismType", mechanismType},
|
||||
{"inputName", "sliderX"},
|
||||
{"inputRange", {{"min", inputRange.first}, {"max", inputRange.second}}},
|
||||
{"frames", frames_json},
|
||||
{"frame_count", frameCount},
|
||||
{"duration", duration},
|
||||
{"stepTime", stepTime},
|
||||
{"startValue", startSliderX},
|
||||
{"endValue", endSliderX},
|
||||
{"startSliderX", startSliderX},
|
||||
{"endSliderX", endSliderX},
|
||||
{"trajectory", buildTrajectoryJson(mechanism->getTrajectoryPoints())},
|
||||
{"slider_trajectory", buildTrajectoryJson(mechanism->getSliderTrajectory())},
|
||||
{"alternative_trajectory", buildTrajectoryJson(mechanism->getAlternativeTrajectory())},
|
||||
{"parameters", {{"mechanismType", mechanismType}, {"L_AB", L_AB}, {"L_BS", L_BS}, {"S_OFS", S_OFS}, {"startSliderX", startSliderX}, {"endSliderX", endSliderX}, {"duration", duration}, {"stepTime", stepTime}}}};
|
||||
}
|
||||
|
||||
if (mechanismType == "RRRR")
|
||||
{
|
||||
const double L1 = valueAny(req_param, {"L1", "L_AB"}, 1.0);
|
||||
const double L2 = valueAny(req_param, {"L2", "L_BS"}, 3.0);
|
||||
const double L3 = req_param.value("L3", 2.5);
|
||||
const double L4 = req_param.value("L4", 3.5);
|
||||
const double startAngleDeg = valueAny(req_param, {"startAngleDeg", "startValue", "angleDeg"}, 0.0);
|
||||
const double endAngleDeg = valueAny(req_param, {"endAngleDeg", "endValue"}, 180.0);
|
||||
|
||||
std::unique_ptr<FourBarMechanism> mechanism(createFourBarMechanism());
|
||||
mechanism->setLink(L1, L2, L3, L4);
|
||||
|
||||
json validationError = validateOrError(mechanism->validateParameters());
|
||||
if (!validationError.is_null())
|
||||
{
|
||||
return validationError;
|
||||
}
|
||||
|
||||
json frames_json = buildSimulationFrames(frameCount, duration, stepTime, startAngleDeg, endAngleDeg, "angleDeg", [&](double value) {
|
||||
return mechanism->calculate(value);
|
||||
});
|
||||
if (!frames_json.is_array())
|
||||
{
|
||||
return frames_json;
|
||||
}
|
||||
|
||||
return {
|
||||
{"success", true},
|
||||
{"mechanismType", mechanismType},
|
||||
{"inputName", "angleDeg"},
|
||||
{"frames", frames_json},
|
||||
{"frame_count", frameCount},
|
||||
{"duration", duration},
|
||||
{"stepTime", stepTime},
|
||||
{"startValue", startAngleDeg},
|
||||
{"endValue", endAngleDeg},
|
||||
{"startAngleDeg", startAngleDeg},
|
||||
{"endAngleDeg", endAngleDeg},
|
||||
{"trajectory", buildTrajectoryJson(mechanism->getTrajectoryPoints())},
|
||||
{"trajectory_c", buildTrajectoryJson(mechanism->getTrajectoryC())},
|
||||
{"crank_circle", buildTrajectoryJson(mechanism->getCrankCirclePoints())},
|
||||
{"parameters", {{"mechanismType", mechanismType}, {"L1", L1}, {"L2", L2}, {"L3", L3}, {"L4", L4}, {"startAngleDeg", startAngleDeg}, {"endAngleDeg", endAngleDeg}, {"duration", duration}, {"stepTime", stepTime}}}};
|
||||
}
|
||||
|
||||
if (mechanismType == "RPRR")
|
||||
{
|
||||
const double OA = valueAny(req_param, {"OA", "L_AB"}, 1.0);
|
||||
const double AB = valueAny(req_param, {"AB", "L_BS"}, 3.0);
|
||||
const double OC = valueAny(req_param, {"OC", "L4"}, 3.0);
|
||||
const double startAngleDeg = valueAny(req_param, {"startAngleDeg", "startValue", "angleDeg"}, 0.0);
|
||||
const double endAngleDeg = valueAny(req_param, {"endAngleDeg", "endValue"}, 180.0);
|
||||
|
||||
CrankRockingBlockMechanism_Forward mechanism;
|
||||
mechanism.setLink(OA, AB, OC);
|
||||
|
||||
json validationError = validateOrError(mechanism.validateParameters());
|
||||
if (!validationError.is_null())
|
||||
{
|
||||
return validationError;
|
||||
}
|
||||
|
||||
json frames_json = buildSimulationFrames(frameCount, duration, stepTime, startAngleDeg, endAngleDeg, "angleDeg", [&](double value) {
|
||||
return mechanism.calculate(value);
|
||||
});
|
||||
if (!frames_json.is_array())
|
||||
{
|
||||
return frames_json;
|
||||
}
|
||||
|
||||
return {
|
||||
{"success", true},
|
||||
{"mechanismType", mechanismType},
|
||||
{"inputName", "angleDeg"},
|
||||
{"frames", frames_json},
|
||||
{"frame_count", frameCount},
|
||||
{"duration", duration},
|
||||
{"stepTime", stepTime},
|
||||
{"startValue", startAngleDeg},
|
||||
{"endValue", endAngleDeg},
|
||||
{"startAngleDeg", startAngleDeg},
|
||||
{"endAngleDeg", endAngleDeg},
|
||||
{"trajectory", buildTrajectoryJson(mechanism.getTrajectoryPoints())},
|
||||
{"crank_circle", buildTrajectoryJson(mechanism.getCrankCirclePoints())},
|
||||
{"rocker_trajectory", buildTrajectoryJson(mechanism.getRockerTrajectory())},
|
||||
{"parameters", {{"mechanismType", mechanismType}, {"OA", OA}, {"AB", AB}, {"OC", OC}, {"startAngleDeg", startAngleDeg}, {"endAngleDeg", endAngleDeg}, {"duration", duration}, {"stepTime", stepTime}}}};
|
||||
}
|
||||
|
||||
return buildErrorJson("Unsupported mechanismType: " + mechanismType, json::array({"mechanismType 必须是 PRRR、RPRR、RRRP、RRRR 之一"}));
|
||||
}
|
||||
} // namespace
|
||||
|
||||
json KinematicsWebAPI::handleFourBarCommand(const std::string &req_cmd, const json &req_param)
|
||||
@@ -84,6 +376,11 @@ json KinematicsWebAPI::handleFourBarCommand(const std::string &req_cmd, const js
|
||||
{
|
||||
log("Handling " + req_cmd + " command");
|
||||
|
||||
if (req_cmd == "Cmd_FourBar_Simulate")
|
||||
{
|
||||
return simulateUnifiedFourBar(req_param);
|
||||
}
|
||||
|
||||
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);
|
||||
@@ -111,10 +408,10 @@ json KinematicsWebAPI::handleFourBarCommand(const std::string &req_cmd, const js
|
||||
if (!validation.isValid())
|
||||
{
|
||||
return {
|
||||
{"success", false},
|
||||
{"error", "Invalid parameters"},
|
||||
{"validation_errors", validation.Errors},
|
||||
{"validation_warnings", validation.Warnings}};
|
||||
{"success", false},
|
||||
{"error", "Invalid parameters"},
|
||||
{"validation_errors", validation.Errors},
|
||||
{"validation_warnings", validation.Warnings}};
|
||||
}
|
||||
|
||||
if (req_cmd == "Cmd_FourBar_CrankSlider_Simulate")
|
||||
@@ -164,7 +461,7 @@ json KinematicsWebAPI::handleFourBarCommand(const std::string &req_cmd, const js
|
||||
{"failed_angleDeg", currentAngleDeg}};
|
||||
}
|
||||
|
||||
frames_json.push_back(buildFrameJson(state, frame, currentTime, currentAngleDeg));
|
||||
frames_json.push_back(buildFrameJson(state, frame, currentTime, currentAngleDeg, "angleDeg"));
|
||||
}
|
||||
|
||||
json result;
|
||||
|
||||
Reference in New Issue
Block a user