功能: 完善风电功率方案计算
- 新增计算方案保存与方案二筛选参数 - 支持方案二桨角规则和有效数据导出 - 优化功率曲线绘制和设计功率展示
This commit is contained in:
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"listeners": [
|
||||
{
|
||||
"address": "0.0.0.0",
|
||||
"port": 8848,
|
||||
"address": "127.0.0.1",
|
||||
"port": 8080,
|
||||
"https": false
|
||||
}
|
||||
],
|
||||
@@ -29,8 +29,8 @@
|
||||
"enabled": true,
|
||||
"allow_origins": [
|
||||
"http://localhost:5173",
|
||||
"http://localhost:8848",
|
||||
"http://127.0.0.1:8848"
|
||||
"http://localhost:8080",
|
||||
"http://127.0.0.1:8080"
|
||||
],
|
||||
"allow_methods": [
|
||||
"GET",
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include <chrono>
|
||||
#include <cmath>
|
||||
#include <ctime>
|
||||
#include <deque>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <iomanip>
|
||||
@@ -23,6 +24,13 @@ namespace {
|
||||
constexpr int kErrorInvalidRequest = 1001;
|
||||
constexpr int kErrorJobNotFound = 1002;
|
||||
constexpr int kErrorServer = 1003;
|
||||
constexpr const char* kDefaultSchemeId = "scheme_one";
|
||||
constexpr const char* kSchemeTwoId = "scheme_two";
|
||||
constexpr const char* kSchemeOneName = "方案一";
|
||||
constexpr const char* kSchemeTwoName = "方案二";
|
||||
constexpr const char* kSchemeOneDefaultDescription = "通用方案";
|
||||
constexpr const char* kSchemeTwoDefaultDescription =
|
||||
"桨角筛选方案,使用三支叶片角度平均值、600s 平均风速和手填转速/功率参数";
|
||||
|
||||
struct RawRow {
|
||||
std::string time;
|
||||
@@ -40,6 +48,11 @@ struct ValidRow {
|
||||
double wind_speed = 0.0;
|
||||
double active_power = 0.0;
|
||||
double generator_speed = 0.0;
|
||||
double blade_pitch_1 = 0.0;
|
||||
double blade_pitch_2 = 0.0;
|
||||
double blade_pitch_3 = 0.0;
|
||||
double pitch_angle_average = 0.0;
|
||||
double wind_speed_600s_average = 0.0;
|
||||
double tip_speed_ratio = 0.0;
|
||||
};
|
||||
|
||||
@@ -49,6 +62,7 @@ struct RemovedPoint {
|
||||
};
|
||||
|
||||
struct CalculationOptions {
|
||||
std::string scheme_id = kDefaultSchemeId;
|
||||
double rated_power = 4800.0;
|
||||
double rated_wind_speed = 18.0;
|
||||
double power_step = 5.0;
|
||||
@@ -59,6 +73,11 @@ struct CalculationOptions {
|
||||
double iqr_upper_multiplier = 2.0;
|
||||
double minimum_generator_speed = 1.0;
|
||||
double generator_speed_k = 0.9;
|
||||
double grid_connected_speed = 0.0;
|
||||
double rated_generator_speed = 0.0;
|
||||
bool rated_power_provided = false;
|
||||
bool grid_connected_speed_provided = false;
|
||||
bool rated_generator_speed_provided = false;
|
||||
};
|
||||
|
||||
struct EstimatedParams {
|
||||
@@ -73,6 +92,15 @@ struct CurveBin {
|
||||
size_t sample_count = 0;
|
||||
};
|
||||
|
||||
struct SchemeInfo {
|
||||
std::string id;
|
||||
std::string name;
|
||||
std::string description;
|
||||
double grid_connected_speed = 1030.0;
|
||||
double rated_generator_speed = 1755.0;
|
||||
double rated_power = 2000.0;
|
||||
};
|
||||
|
||||
std::string Trim(const std::string& value) {
|
||||
const auto begin = value.find_first_not_of(" \t\r\n");
|
||||
if (begin == std::string::npos) {
|
||||
@@ -103,6 +131,10 @@ fs::path JobRowsPath(const std::string& job_id) {
|
||||
return JobDir(job_id) / "rows.jsonl";
|
||||
}
|
||||
|
||||
fs::path SchemeConfigPath() {
|
||||
return fs::path("data") / "wind_schemes.json";
|
||||
}
|
||||
|
||||
std::string GenerateJobId() {
|
||||
const auto now = std::chrono::system_clock::now().time_since_epoch().count();
|
||||
std::random_device rd;
|
||||
@@ -148,6 +180,117 @@ std::optional<std::string> GetStringField(const json& body, const std::string& f
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<double> GetNumberField(const json& body, const std::string& field) {
|
||||
if (!body.contains(field) || !body[field].is_number()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
return body[field].get<double>();
|
||||
}
|
||||
|
||||
std::string NormalizeSchemeId(const std::string& scheme_id) {
|
||||
return scheme_id == kSchemeTwoId ? kSchemeTwoId : kDefaultSchemeId;
|
||||
}
|
||||
|
||||
std::vector<SchemeInfo> DefaultSchemes() {
|
||||
return {
|
||||
{kDefaultSchemeId, kSchemeOneName, kSchemeOneDefaultDescription},
|
||||
{kSchemeTwoId, kSchemeTwoName, kSchemeTwoDefaultDescription, 1030.0, 1755.0, 2000.0},
|
||||
};
|
||||
}
|
||||
|
||||
std::optional<SchemeInfo> FindScheme(const std::vector<SchemeInfo>& schemes,
|
||||
const std::string& scheme_id) {
|
||||
const std::string normalized_id = NormalizeSchemeId(scheme_id);
|
||||
for (const auto& scheme : schemes) {
|
||||
if (scheme.id == normalized_id) {
|
||||
return scheme;
|
||||
}
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
json SchemeToJson(const SchemeInfo& scheme) {
|
||||
json data;
|
||||
data["id"] = scheme.id;
|
||||
data["name"] = scheme.name;
|
||||
data["description"] = scheme.description;
|
||||
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;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
std::vector<SchemeInfo> LoadSchemes() {
|
||||
auto schemes = DefaultSchemes();
|
||||
const auto config_path = SchemeConfigPath();
|
||||
if (!fs::exists(config_path)) {
|
||||
return schemes;
|
||||
}
|
||||
|
||||
try {
|
||||
std::ifstream in(config_path);
|
||||
const auto saved = json::parse(in);
|
||||
if (!saved.contains("schemes") || !saved["schemes"].is_array()) {
|
||||
return schemes;
|
||||
}
|
||||
|
||||
for (auto& scheme : schemes) {
|
||||
for (const auto& item : saved["schemes"]) {
|
||||
const auto saved_id = GetStringField(item, "id");
|
||||
if (!saved_id.has_value() || saved_id.value() != scheme.id) {
|
||||
continue;
|
||||
}
|
||||
const auto description = GetStringField(item, "description");
|
||||
if (description.has_value()) {
|
||||
scheme.description = description.value();
|
||||
}
|
||||
if (scheme.id == kSchemeTwoId &&
|
||||
item.contains("parameters") &&
|
||||
item["parameters"].is_object()) {
|
||||
const auto& params = item["parameters"];
|
||||
if (const auto value = GetNumberField(params, "grid_connected_speed");
|
||||
value.has_value() && value.value() > 0.0) {
|
||||
scheme.grid_connected_speed = value.value();
|
||||
}
|
||||
if (const auto value = GetNumberField(params, "rated_generator_speed");
|
||||
value.has_value() && value.value() > 0.0) {
|
||||
scheme.rated_generator_speed = value.value();
|
||||
}
|
||||
if (const auto value = GetNumberField(params, "rated_power");
|
||||
value.has_value() && value.value() > 0.0) {
|
||||
scheme.rated_power = value.value();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (const std::exception&) {
|
||||
return schemes;
|
||||
}
|
||||
return schemes;
|
||||
}
|
||||
|
||||
bool SaveSchemes(const std::vector<SchemeInfo>& schemes) {
|
||||
try {
|
||||
const auto config_path = SchemeConfigPath();
|
||||
fs::create_directories(config_path.parent_path());
|
||||
|
||||
json data;
|
||||
data["default_scheme_id"] = kDefaultSchemeId;
|
||||
data["schemes"] = json::array();
|
||||
for (const auto& scheme : schemes) {
|
||||
data["schemes"].push_back(SchemeToJson(scheme));
|
||||
}
|
||||
|
||||
std::ofstream out(config_path);
|
||||
out << data.dump(2);
|
||||
return true;
|
||||
} catch (const std::exception&) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<double> GetDoubleField(const json& body, const std::string& field) {
|
||||
if (!body.contains(field)) {
|
||||
return std::nullopt;
|
||||
@@ -261,9 +404,13 @@ CalculationOptions ParseOptions(const json& body) {
|
||||
}
|
||||
|
||||
const auto& opt = body["options"];
|
||||
if (const auto value = GetStringField(opt, "scheme_id"); value.has_value()) {
|
||||
options.scheme_id = NormalizeSchemeId(value.value());
|
||||
}
|
||||
if (const auto value = GetDoubleField(opt, "rated_power");
|
||||
value.has_value() && value.value() > 0.0) {
|
||||
options.rated_power = value.value();
|
||||
options.rated_power_provided = true;
|
||||
}
|
||||
if (const auto value = GetDoubleField(opt, "rated_wind_speed");
|
||||
value.has_value() && value.value() > 0.0) {
|
||||
@@ -301,13 +448,43 @@ CalculationOptions ParseOptions(const json& body) {
|
||||
value.has_value() && value.value() >= 0.0) {
|
||||
options.generator_speed_k = value.value();
|
||||
}
|
||||
if (const auto value = GetDoubleField(opt, "grid_connected_speed");
|
||||
value.has_value() && value.value() > 0.0) {
|
||||
options.grid_connected_speed = value.value();
|
||||
options.grid_connected_speed_provided = true;
|
||||
}
|
||||
if (const auto value = GetDoubleField(opt, "rated_generator_speed");
|
||||
value.has_value() && value.value() > 0.0) {
|
||||
options.rated_generator_speed = value.value();
|
||||
options.rated_generator_speed_provided = true;
|
||||
}
|
||||
return options;
|
||||
}
|
||||
|
||||
bool IsSchemeTwo(const CalculationOptions& options) {
|
||||
return options.scheme_id == kSchemeTwoId;
|
||||
}
|
||||
|
||||
void AddInvalid(std::unordered_map<std::string, int>& counters, const std::string& reason) {
|
||||
counters[reason] += 1;
|
||||
}
|
||||
|
||||
void ComputeWindSpeed600sAverage(std::vector<ValidRow>& rows) {
|
||||
std::deque<ValidRow> window;
|
||||
double wind_speed_sum = 0.0;
|
||||
for (auto& row : rows) {
|
||||
while (!window.empty() &&
|
||||
std::difftime(row.timestamp, window.front().timestamp) > 600.0) {
|
||||
wind_speed_sum -= window.front().wind_speed;
|
||||
window.pop_front();
|
||||
}
|
||||
|
||||
window.push_back(row);
|
||||
wind_speed_sum += row.wind_speed;
|
||||
row.wind_speed_600s_average = wind_speed_sum / static_cast<double>(window.size());
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<ValidRow> FilterLimitPower(const std::vector<ValidRow>& rows,
|
||||
const CalculationOptions& options,
|
||||
int& removed_count,
|
||||
@@ -362,6 +539,68 @@ std::vector<ValidRow> FilterLimitPower(const std::vector<ValidRow>& rows,
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<ValidRow> FilterSchemeTwoGridSpeed(const std::vector<ValidRow>& rows,
|
||||
const CalculationOptions& options,
|
||||
int& removed_count,
|
||||
std::vector<RemovedPoint>& removed_points) {
|
||||
removed_count = 0;
|
||||
if (!IsSchemeTwo(options) || rows.empty()) {
|
||||
return rows;
|
||||
}
|
||||
|
||||
std::vector<ValidRow> result;
|
||||
result.reserve(rows.size());
|
||||
for (const auto& row : rows) {
|
||||
if (row.generator_speed <= options.grid_connected_speed) {
|
||||
++removed_count;
|
||||
removed_points.push_back(RemovedPoint{row, "scheme_two_grid_speed_low"});
|
||||
} else {
|
||||
result.push_back(row);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<ValidRow> FilterSchemeTwoPitchRules(
|
||||
const std::vector<ValidRow>& rows,
|
||||
const CalculationOptions& options,
|
||||
int& low_power_pitch_wind_count,
|
||||
int& low_speed_pitch_count,
|
||||
int& low_power_pitch_count,
|
||||
std::vector<RemovedPoint>& removed_points) {
|
||||
low_power_pitch_wind_count = 0;
|
||||
low_speed_pitch_count = 0;
|
||||
low_power_pitch_count = 0;
|
||||
if (!IsSchemeTwo(options) || rows.empty()) {
|
||||
return rows;
|
||||
}
|
||||
|
||||
std::vector<ValidRow> result;
|
||||
result.reserve(rows.size());
|
||||
for (const auto& row : rows) {
|
||||
if (row.active_power < options.rated_power &&
|
||||
row.pitch_angle_average > 5.0 &&
|
||||
row.wind_speed_600s_average < 15.0) {
|
||||
++low_power_pitch_wind_count;
|
||||
removed_points.push_back(RemovedPoint{row, "scheme_two_low_power_pitch_wind"});
|
||||
continue;
|
||||
}
|
||||
if (row.generator_speed < options.rated_generator_speed &&
|
||||
row.pitch_angle_average > 5.0) {
|
||||
++low_speed_pitch_count;
|
||||
removed_points.push_back(RemovedPoint{row, "scheme_two_low_speed_pitch"});
|
||||
continue;
|
||||
}
|
||||
if (row.active_power < 1980.0 && row.pitch_angle_average > 2.0) {
|
||||
++low_power_pitch_count;
|
||||
removed_points.push_back(RemovedPoint{row, "scheme_two_low_power_pitch"});
|
||||
continue;
|
||||
}
|
||||
result.push_back(row);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename ValueGetter>
|
||||
std::vector<ValidRow> FilterByWindBinIqr(const std::vector<ValidRow>& rows,
|
||||
const CalculationOptions& options,
|
||||
@@ -674,6 +913,100 @@ std::string DatePart(const std::string& time_text) {
|
||||
|
||||
} // namespace
|
||||
|
||||
void WindPowerController::GetSchemes(
|
||||
const HttpRequestPtr&,
|
||||
std::function<void(const HttpResponsePtr&)>&& callback) {
|
||||
json data;
|
||||
data["default_scheme_id"] = kDefaultSchemeId;
|
||||
data["schemes"] = json::array();
|
||||
for (const auto& scheme : LoadSchemes()) {
|
||||
data["schemes"].push_back(SchemeToJson(scheme));
|
||||
}
|
||||
SendSuccess(callback, data);
|
||||
}
|
||||
|
||||
void WindPowerController::SaveSchemeDescription(
|
||||
const HttpRequestPtr& req,
|
||||
std::function<void(const HttpResponsePtr&)>&& callback,
|
||||
const std::string& scheme_id) {
|
||||
const std::string normalized_id = NormalizeSchemeId(scheme_id);
|
||||
if (scheme_id != normalized_id) {
|
||||
SendError(callback, kErrorInvalidRequest, "方案不存在");
|
||||
return;
|
||||
}
|
||||
|
||||
std::string error;
|
||||
const auto body = ParseBody(req, error);
|
||||
if (!body.has_value()) {
|
||||
SendError(callback, kErrorInvalidRequest, error);
|
||||
return;
|
||||
}
|
||||
|
||||
const auto description = GetStringField(body.value(), "description");
|
||||
if (!description.has_value()) {
|
||||
SendError(callback, kErrorInvalidRequest, "方案描述不能为空");
|
||||
return;
|
||||
}
|
||||
if (description.value().size() > 1000) {
|
||||
SendError(callback, kErrorInvalidRequest, "方案描述不能超过 1000 个字符");
|
||||
return;
|
||||
}
|
||||
|
||||
std::optional<double> grid_connected_speed;
|
||||
std::optional<double> rated_generator_speed;
|
||||
std::optional<double> rated_power;
|
||||
if (normalized_id == kSchemeTwoId && body.value().contains("parameters")) {
|
||||
if (!body.value()["parameters"].is_object()) {
|
||||
SendError(callback, kErrorInvalidRequest, "方案参数格式错误");
|
||||
return;
|
||||
}
|
||||
const auto& params = body.value()["parameters"];
|
||||
grid_connected_speed = GetDoubleField(params, "grid_connected_speed");
|
||||
rated_generator_speed = GetDoubleField(params, "rated_generator_speed");
|
||||
rated_power = GetDoubleField(params, "rated_power");
|
||||
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) {
|
||||
SendError(callback, kErrorInvalidRequest, "方案二参数必须为正数");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
auto schemes = LoadSchemes();
|
||||
bool updated = false;
|
||||
for (auto& scheme : schemes) {
|
||||
if (scheme.id == normalized_id) {
|
||||
scheme.description = description.value();
|
||||
if (scheme.id == kSchemeTwoId) {
|
||||
if (grid_connected_speed.has_value()) {
|
||||
scheme.grid_connected_speed = grid_connected_speed.value();
|
||||
}
|
||||
if (rated_generator_speed.has_value()) {
|
||||
scheme.rated_generator_speed = rated_generator_speed.value();
|
||||
}
|
||||
if (rated_power.has_value()) {
|
||||
scheme.rated_power = rated_power.value();
|
||||
}
|
||||
}
|
||||
updated = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!updated) {
|
||||
SendError(callback, kErrorInvalidRequest, "方案不存在");
|
||||
return;
|
||||
}
|
||||
if (!SaveSchemes(schemes)) {
|
||||
SendError(callback, kErrorServer, "保存方案描述失败");
|
||||
return;
|
||||
}
|
||||
|
||||
const auto saved_scheme = FindScheme(schemes, normalized_id);
|
||||
json data;
|
||||
data["scheme"] = SchemeToJson(saved_scheme.value());
|
||||
SendSuccess(callback, data);
|
||||
}
|
||||
|
||||
void WindPowerController::StartJob(
|
||||
const HttpRequestPtr& req,
|
||||
std::function<void(const HttpResponsePtr&)>&& callback) {
|
||||
@@ -769,6 +1102,14 @@ void WindPowerController::FinishJob(
|
||||
}
|
||||
|
||||
const CalculationOptions options = ParseOptions(*body);
|
||||
if (IsSchemeTwo(options) &&
|
||||
(!options.rated_power_provided ||
|
||||
!options.grid_connected_speed_provided ||
|
||||
!options.rated_generator_speed_provided)) {
|
||||
SendError(callback, kErrorInvalidRequest, "方案二需要填写并网转速、额定转速和额定功率");
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<ValidRow> parsed_rows;
|
||||
std::unordered_map<std::string, int> invalid_reasons;
|
||||
int raw_rows = 0;
|
||||
@@ -795,6 +1136,9 @@ void WindPowerController::FinishJob(
|
||||
const auto wind_speed = GetDoubleField(row, "wind_speed");
|
||||
const auto active_power = GetDoubleField(row, "active_power");
|
||||
const auto generator_speed = GetDoubleField(row, "generator_speed");
|
||||
const auto blade_pitch_1 = GetDoubleField(row, "blade_pitch_1");
|
||||
const auto blade_pitch_2 = GetDoubleField(row, "blade_pitch_2");
|
||||
const auto blade_pitch_3 = GetDoubleField(row, "blade_pitch_3");
|
||||
|
||||
if (!active_power.has_value() || !std::isfinite(active_power.value()) ||
|
||||
active_power.value() <= 0.0) {
|
||||
@@ -807,6 +1151,13 @@ void WindPowerController::FinishJob(
|
||||
AddInvalid(invalid_reasons, "invalid_generator_speed");
|
||||
continue;
|
||||
}
|
||||
if (IsSchemeTwo(options) &&
|
||||
(!blade_pitch_1.has_value() || !std::isfinite(blade_pitch_1.value()) ||
|
||||
!blade_pitch_2.has_value() || !std::isfinite(blade_pitch_2.value()) ||
|
||||
!blade_pitch_3.has_value() || !std::isfinite(blade_pitch_3.value()))) {
|
||||
AddInvalid(invalid_reasons, "invalid_pitch_angle");
|
||||
continue;
|
||||
}
|
||||
if (!time_text.has_value()) {
|
||||
AddInvalid(invalid_reasons, "invalid_time");
|
||||
continue;
|
||||
@@ -834,6 +1185,15 @@ void WindPowerController::FinishJob(
|
||||
valid_row.wind_speed = wind_speed.value();
|
||||
valid_row.active_power = active_power.value();
|
||||
valid_row.generator_speed = generator_speed.value();
|
||||
if (IsSchemeTwo(options)) {
|
||||
valid_row.blade_pitch_1 = blade_pitch_1.value();
|
||||
valid_row.blade_pitch_2 = blade_pitch_2.value();
|
||||
valid_row.blade_pitch_3 = blade_pitch_3.value();
|
||||
valid_row.pitch_angle_average =
|
||||
(valid_row.blade_pitch_1 +
|
||||
valid_row.blade_pitch_2 +
|
||||
valid_row.blade_pitch_3) / 3.0;
|
||||
}
|
||||
parsed_rows.push_back(valid_row);
|
||||
}
|
||||
} catch (const std::exception&) {
|
||||
@@ -889,13 +1249,27 @@ void WindPowerController::FinishJob(
|
||||
int high_wind_low_power_count = 0;
|
||||
int curve_residual_outlier_count = 0;
|
||||
int rated_plateau_low_power_count = 0;
|
||||
int scheme_two_grid_speed_low_count = 0;
|
||||
int scheme_two_low_power_pitch_wind_count = 0;
|
||||
int scheme_two_low_speed_pitch_count = 0;
|
||||
int scheme_two_low_power_pitch_count = 0;
|
||||
int cleaned_rows_count = 0;
|
||||
for (const auto& fan_id : fan_ids) {
|
||||
fans.push_back(fan_id);
|
||||
auto fan_rows = rows_by_fan[fan_id];
|
||||
std::vector<RemovedPoint> fan_removed_points;
|
||||
if (IsSchemeTwo(options)) {
|
||||
ComputeWindSpeed600sAverage(fan_rows);
|
||||
}
|
||||
|
||||
int removed = 0;
|
||||
fan_rows = FilterSchemeTwoGridSpeed(
|
||||
fan_rows,
|
||||
options,
|
||||
removed,
|
||||
fan_removed_points);
|
||||
scheme_two_grid_speed_low_count += removed;
|
||||
|
||||
fan_rows = FilterLimitPower(fan_rows, options, removed, fan_removed_points);
|
||||
limit_power_count += removed;
|
||||
|
||||
@@ -951,13 +1325,31 @@ void WindPowerController::FinishJob(
|
||||
fan_removed_points);
|
||||
rated_plateau_low_power_count += removed;
|
||||
|
||||
int low_power_pitch_wind_removed = 0;
|
||||
int low_speed_pitch_removed = 0;
|
||||
int low_power_pitch_removed = 0;
|
||||
fan_rows = FilterSchemeTwoPitchRules(
|
||||
fan_rows,
|
||||
options,
|
||||
low_power_pitch_wind_removed,
|
||||
low_speed_pitch_removed,
|
||||
low_power_pitch_removed,
|
||||
fan_removed_points);
|
||||
scheme_two_low_power_pitch_wind_count += low_power_pitch_wind_removed;
|
||||
scheme_two_low_speed_pitch_count += low_speed_pitch_removed;
|
||||
scheme_two_low_power_pitch_count += low_power_pitch_removed;
|
||||
|
||||
cleaned_rows_count += static_cast<int>(fan_rows.size());
|
||||
|
||||
json fan_scatter = json::array();
|
||||
for (const auto& row : fan_rows) {
|
||||
json point;
|
||||
point["fan_id"] = row.fan_id;
|
||||
point["time"] = row.time;
|
||||
point["wind_speed"] = row.wind_speed;
|
||||
point["active_power"] = row.active_power;
|
||||
point["generator_speed"] = row.generator_speed;
|
||||
point["pitch_angle_average"] = row.pitch_angle_average;
|
||||
fan_scatter.push_back(point);
|
||||
}
|
||||
scatter_points[fan_id] = fan_scatter;
|
||||
@@ -1012,6 +1404,11 @@ void WindPowerController::FinishJob(
|
||||
invalid_reasons["high_wind_low_power"] = high_wind_low_power_count;
|
||||
invalid_reasons["curve_residual_outlier"] = curve_residual_outlier_count;
|
||||
invalid_reasons["rated_plateau_low_power"] = rated_plateau_low_power_count;
|
||||
invalid_reasons["scheme_two_grid_speed_low"] = scheme_two_grid_speed_low_count;
|
||||
invalid_reasons["scheme_two_low_power_pitch_wind"] =
|
||||
scheme_two_low_power_pitch_wind_count;
|
||||
invalid_reasons["scheme_two_low_speed_pitch"] = scheme_two_low_speed_pitch_count;
|
||||
invalid_reasons["scheme_two_low_power_pitch"] = scheme_two_low_power_pitch_count;
|
||||
|
||||
const int invalid_total = raw_rows - cleaned_rows_count;
|
||||
|
||||
@@ -1026,10 +1423,17 @@ void WindPowerController::FinishJob(
|
||||
summary["high_wind_low_power_rows"] = high_wind_low_power_count;
|
||||
summary["curve_residual_outlier_rows"] = curve_residual_outlier_count;
|
||||
summary["rated_plateau_low_power_rows"] = rated_plateau_low_power_count;
|
||||
summary["scheme_two_grid_speed_low_rows"] = scheme_two_grid_speed_low_count;
|
||||
summary["scheme_two_low_power_pitch_wind_rows"] =
|
||||
scheme_two_low_power_pitch_wind_count;
|
||||
summary["scheme_two_low_speed_pitch_rows"] = scheme_two_low_speed_pitch_count;
|
||||
summary["scheme_two_low_power_pitch_rows"] = scheme_two_low_power_pitch_count;
|
||||
summary["invalid_reasons"] = CounterJson(invalid_reasons);
|
||||
summary["fan_count"] = fans.size();
|
||||
|
||||
json data;
|
||||
const auto selected_scheme = FindScheme(LoadSchemes(), options.scheme_id);
|
||||
data["scheme"] = SchemeToJson(selected_scheme.value());
|
||||
data["summary"] = summary;
|
||||
data["fans"] = fans;
|
||||
data["curves"] = curves;
|
||||
|
||||
@@ -15,8 +15,17 @@ public:
|
||||
ADD_METHOD_TO(WindPowerController::UploadChunk, "/api/wind/jobs/chunk", Post);
|
||||
ADD_METHOD_TO(WindPowerController::FinishJob, "/api/wind/jobs/finish", Post);
|
||||
ADD_METHOD_TO(WindPowerController::DeleteJob, "/api/wind/jobs/{1}", Delete);
|
||||
ADD_METHOD_TO(WindPowerController::GetSchemes, "/api/wind/schemes", Get);
|
||||
ADD_METHOD_TO(WindPowerController::SaveSchemeDescription,
|
||||
"/api/wind/schemes/{1}/description",
|
||||
Post);
|
||||
METHOD_LIST_END
|
||||
|
||||
void GetSchemes(const HttpRequestPtr& req,
|
||||
std::function<void(const HttpResponsePtr&)>&& callback);
|
||||
void SaveSchemeDescription(const HttpRequestPtr& req,
|
||||
std::function<void(const HttpResponsePtr&)>&& callback,
|
||||
const std::string& scheme_id);
|
||||
void StartJob(const HttpRequestPtr& req,
|
||||
std::function<void(const HttpResponsePtr&)>&& callback);
|
||||
void UploadChunk(const HttpRequestPtr& req,
|
||||
|
||||
Reference in New Issue
Block a user