修复: 升级框架并完善报告导出

- 升级 Drogon 和 Trantor,修复畸形请求导致的连接计数泄漏\n- 增加第三方框架版本校验与自动重建\n- 完善完整报告导出和接口文档
This commit is contained in:
cloud
2026-08-10 09:50:09 +08:00
parent 99ed321d24
commit 0e28826073
82 changed files with 3095 additions and 566 deletions
@@ -78,6 +78,7 @@ struct CalculationOptions {
double power_step = 5.0;
double cleaning_wind_speed_step = 0.25;
double curve_wind_speed_step = 0.5;
double report_wind_speed_interval = 0.25;
double wind_speed_change_threshold = 1.0;
double iqr_lower_multiplier = 1.2;
double iqr_upper_multiplier = 2.0;
@@ -119,9 +120,11 @@ struct SchemeInfo {
double scheme_one_generator_speed_k = 0.9;
double scheme_one_rotor_radius = 78.0;
double scheme_one_gearbox_ratio = 162.0;
double scheme_one_report_wind_speed_interval = 0.25;
double grid_connected_speed = 1030.0;
double rated_generator_speed = 1755.0;
double rated_power = 2000.0;
double scheme_two_report_wind_speed_interval = 0.25;
};
constexpr double kRatedCornerWindBefore = 0.5;
@@ -527,11 +530,14 @@ json SchemeToJson(const SchemeInfo& scheme) {
{"generator_speed_k", scheme.scheme_one_generator_speed_k},
{"rotor_radius", scheme.scheme_one_rotor_radius},
{"gearbox_ratio", scheme.scheme_one_gearbox_ratio},
{"report_wind_speed_interval", scheme.scheme_one_report_wind_speed_interval},
};
} else if (scheme.id == kSchemeTwoId) {
data["parameters"]["grid_connected_speed"] = scheme.grid_connected_speed;
data["parameters"]["rated_generator_speed"] = scheme.rated_generator_speed;
data["parameters"]["rated_power"] = scheme.rated_power;
data["parameters"]["report_wind_speed_interval"] =
scheme.scheme_two_report_wind_speed_interval;
}
return data;
}
@@ -590,6 +596,8 @@ std::vector<SchemeInfo> LoadSchemes() {
load_non_negative("generator_speed_k", scheme.scheme_one_generator_speed_k);
load_positive("rotor_radius", scheme.scheme_one_rotor_radius);
load_positive("gearbox_ratio", scheme.scheme_one_gearbox_ratio);
load_positive("report_wind_speed_interval",
scheme.scheme_one_report_wind_speed_interval);
} else if (scheme.id == kSchemeTwoId) {
if (const auto value = GetNumberField(params, "grid_connected_speed");
value.has_value() && value.value() > 0.0) {
@@ -603,6 +611,10 @@ std::vector<SchemeInfo> LoadSchemes() {
value.has_value() && value.value() > 0.0) {
scheme.rated_power = value.value();
}
if (const auto value = GetNumberField(params, "report_wind_speed_interval");
value.has_value() && value.value() > 0.0 && value.value() <= 2.0) {
scheme.scheme_two_report_wind_speed_interval = value.value();
}
}
}
}
@@ -790,6 +802,10 @@ CalculationOptions ParseOptions(const json& body) {
value.has_value() && value.value() > 0.0 && value.value() <= 2.0) {
options.curve_wind_speed_step = value.value();
}
if (const auto value = GetDoubleField(opt, "report_wind_speed_interval");
value.has_value() && value.value() > 0.0 && value.value() <= 2.0) {
options.report_wind_speed_interval = value.value();
}
if (const auto value = GetDoubleField(opt, "wind_speed_change_threshold");
value.has_value() && value.value() >= 0.0) {
options.wind_speed_change_threshold = value.value();
@@ -1359,6 +1375,7 @@ void WindPowerController::SaveSchemeDescription(
std::optional<double> grid_connected_speed;
std::optional<double> rated_generator_speed;
std::optional<double> rated_power;
std::optional<double> report_wind_speed_interval;
if (normalized_id == kDefaultSchemeId) {
if (!body.value().contains("parameters") || !body.value()["parameters"].is_object()) {
SendError(callback, kErrorInvalidRequest, "方案一参数格式错误");
@@ -1376,6 +1393,7 @@ void WindPowerController::SaveSchemeDescription(
if (!valid_positive("rated_power") || !valid_positive("rated_wind_speed") ||
!valid_positive("power_step") || !valid_positive("cleaning_wind_speed_step") ||
!valid_positive("rotor_radius") || !valid_positive("gearbox_ratio") ||
!valid_positive("report_wind_speed_interval") ||
!valid_non_negative("wind_speed_change_threshold") ||
!valid_non_negative("iqr_lower_multiplier") ||
!valid_non_negative("iqr_upper_multiplier") ||
@@ -1385,6 +1403,7 @@ void WindPowerController::SaveSchemeDescription(
return;
}
if (GetDoubleField(params, "cleaning_wind_speed_step").value() > 2.0 ||
GetDoubleField(params, "report_wind_speed_interval").value() > 2.0 ||
GetDoubleField(params, "iqr_lower_multiplier").value() > 10.0 ||
GetDoubleField(params, "iqr_upper_multiplier").value() > 10.0) {
SendError(callback, kErrorInvalidRequest, "方案一参数超出允许范围");
@@ -1400,9 +1419,12 @@ void WindPowerController::SaveSchemeDescription(
grid_connected_speed = GetDoubleField(params, "grid_connected_speed");
rated_generator_speed = GetDoubleField(params, "rated_generator_speed");
rated_power = GetDoubleField(params, "rated_power");
report_wind_speed_interval = GetDoubleField(params, "report_wind_speed_interval");
if (!grid_connected_speed.has_value() || grid_connected_speed.value() <= 0.0 ||
!rated_generator_speed.has_value() || rated_generator_speed.value() <= 0.0 ||
!rated_power.has_value() || rated_power.value() <= 0.0) {
!rated_power.has_value() || rated_power.value() <= 0.0 ||
!report_wind_speed_interval.has_value() || report_wind_speed_interval.value() <= 0.0 ||
report_wind_speed_interval.value() > 2.0) {
SendError(callback, kErrorInvalidRequest, "方案二参数必须为正数");
return;
}
@@ -1432,6 +1454,8 @@ void WindPowerController::SaveSchemeDescription(
GetDoubleField(params, "generator_speed_k").value();
scheme.scheme_one_rotor_radius = GetDoubleField(params, "rotor_radius").value();
scheme.scheme_one_gearbox_ratio = GetDoubleField(params, "gearbox_ratio").value();
scheme.scheme_one_report_wind_speed_interval =
GetDoubleField(params, "report_wind_speed_interval").value();
} else if (scheme.id == kSchemeTwoId) {
if (grid_connected_speed.has_value()) {
scheme.grid_connected_speed = grid_connected_speed.value();
@@ -1442,6 +1466,10 @@ void WindPowerController::SaveSchemeDescription(
if (rated_power.has_value()) {
scheme.rated_power = rated_power.value();
}
if (report_wind_speed_interval.has_value()) {
scheme.scheme_two_report_wind_speed_interval =
report_wind_speed_interval.value();
}
}
updated = true;
break;
@@ -1619,6 +1647,16 @@ void WindPowerController::FinishJob(
TaskReleaseGuard finish_guard(job_id.value());
const CalculationOptions options = ParseOptions(*body);
if (body->contains("options") && (*body)["options"].is_object() &&
(*body)["options"].contains("report_wind_speed_interval")) {
const auto interval = GetDoubleField((*body)["options"], "report_wind_speed_interval");
if (!interval.has_value() || !std::isfinite(interval.value()) ||
interval.value() <= 0.0 || interval.value() > 2.0) {
SendError(callback, kErrorInvalidRequest,
"报告公式风速区间半宽必须大于 0 且不超过 2");
return;
}
}
if (IsSchemeTwo(options) &&
(!options.rated_power_provided ||
!options.grid_connected_speed_provided ||
@@ -1964,6 +2002,8 @@ void WindPowerController::FinishJob(
json data;
const auto selected_scheme = FindScheme(LoadSchemes(), options.scheme_id);
data["scheme"] = SchemeToJson(selected_scheme.value());
data["scheme"]["parameters"]["report_wind_speed_interval"] =
options.report_wind_speed_interval;
data["summary"] = summary;
data["fans"] = fans;
data["curves"] = curves;
@@ -2108,25 +2148,36 @@ void WindPowerController::ExportReport(
worksheet_set_column(curve, i, i, i == 0 ? 9 : 17, nullptr);
}
const auto raw_wind = ExcelColumnName(wind_column);
const auto report_interval = result.value("scheme", json::object())
.value("parameters", json::object())
.value("report_wind_speed_interval", 0.25);
const auto interval_text = std::to_string(report_interval);
const auto report_row_count = (*body)["report_rows"].size();
const auto k_last_row = std::max<size_t>(2, report_row_count + 1);
const auto k_formula = "=IFERROR(SUM(F2:F" + std::to_string(k_last_row) +
")/SUM(G2:G" + std::to_string(k_last_row) + "),0)";
lxw_row_t curve_row = 1;
for (const auto& point : (*body)["report_rows"]) {
const auto excel_row = curve_row + 1;
worksheet_write_number(curve, curve_row, 0, excel_row - 1, nullptr);
worksheet_write_number(curve, curve_row, 1, point.value("wind_speed", 0.0), number_format);
const auto frequency = "=(COUNTIFS('筛选前的数据'!" + raw_wind + ":" + raw_wind + ",\">=\"&B" + std::to_string(excel_row) + "-0.5,'筛选前的数据'!" + raw_wind + ":" + raw_wind + ",\"<\"&B" + std::to_string(excel_row) + "+0.5)/COUNT('筛选前的数据'!" + raw_wind + ":" + raw_wind + "))*8760";
const auto frequency = "=(COUNTIFS('筛选前的数据'!" + raw_wind + ":" + raw_wind + ",\">=\"&B" + std::to_string(excel_row) + "-" + interval_text + ",'筛选前的数据'!" + raw_wind + ":" + raw_wind + ",\"<\"&B" + std::to_string(excel_row) + "+" + interval_text + ")/COUNT('筛选前的数据'!" + raw_wind + ":" + raw_wind + "))*8760";
worksheet_write_formula(curve, curve_row, 2, frequency.c_str(), number_format);
const auto actual = "=IFERROR(AVERAGEIFS('筛选后的数据'!$C:$C,'筛选后的数据'!$E:$E,\">=\"&B" + std::to_string(excel_row) + "-0.5,'筛选后的数据'!$E:$E,\"<\"&B" + std::to_string(excel_row) + "+0.5),0)";
const auto actual = "=IFERROR(AVERAGEIFS('筛选后的数据'!$C:$C,'筛选后的数据'!$E:$E,\">=\"&B" + std::to_string(excel_row) + "-" + interval_text + ",'筛选后的数据'!$E:$E,\"<\"&B" + std::to_string(excel_row) + "+" + interval_text + "),0)";
worksheet_write_formula(curve, curve_row, 3, actual.c_str(), number_format);
worksheet_write_number(curve, curve_row, 4, point.value("design_power", 0.0), number_format);
const auto generated = "=IFERROR(ROUND(C" + std::to_string(excel_row) + "*D" + std::to_string(excel_row) + "/1000,4),0)";
const auto theoretical = "=ROUND(C" + std::to_string(excel_row) + "*E" + std::to_string(excel_row) + "/1000,4)";
worksheet_write_formula(curve, curve_row, 5, generated.c_str(), number_format);
worksheet_write_formula(curve, curve_row, 6, theoretical.c_str(), number_format);
if (curve_row == 1) {
worksheet_write_formula(curve, curve_row, 8, k_formula.c_str(), number_format);
}
++curve_row;
}
const auto last_row = std::max<lxw_row_t>(2, curve_row);
const auto k_formula = "=IFERROR(SUM(F2:F" + std::to_string(last_row) + ")/SUM(G2:G" + std::to_string(last_row) + "),0)";
worksheet_write_formula(curve, 1, 8, k_formula.c_str(), number_format);
if (report_row_count == 0) {
worksheet_write_formula(curve, 1, 8, k_formula.c_str(), number_format);
}
worksheet_autofilter(curve, 0, 0, std::max<lxw_row_t>(1, curve_row - 1), 6);
if (body->contains("chart_image") && (*body)["chart_image"].is_string()) {
@@ -2137,7 +2188,11 @@ void WindPowerController::ExportReport(
std::ofstream image(image_path, std::ios::binary | std::ios::trunc);
image << drogon::utils::base64Decode(image_data);
image.close();
worksheet_insert_image(curve, 3, 8, image_path.string().c_str());
lxw_image_options image_options{};
image_options.x_scale = 0.5;
image_options.y_scale = 0.5;
worksheet_insert_image_opt(curve, 3, 8, image_path.string().c_str(),
&image_options);
}
if (workbook_close(workbook) != LXW_NO_ERROR) throw std::runtime_error("写入 Excel 文件失败");
callback(HttpResponse::newFileResponse(report_path.string(),