eis/eqpalg/algs/roller3.cpp
2026-05-09 13:33:07 +08:00

462 lines
15 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "mix_cc/ihyper_db.h"
#include <algorithm>
#include <base/BitTool.h>
#include <eqpalg/algs/roller3.h>
#include <eqpalg/exp_macro/get_macro_replaced_exp.h>
#include <eqpalg/feature_extraction/daa.h>
#include <eqpalg/utility/build_alarm_info.h>
#include <limits>
#include <string>
#include <utility>
#include <vector>
int Roller3::init() {
int res = 0;
try {
res += AlgBase::init();
this->con_monitor_.setThreshold(100);
logger_->Debug() << "离群群方式[-2:%;-3:实际值]:" << exp_type_ << std::endl;
logger_->Debug() << "res:" << res << std::endl;
if (res != 0) {
this->exp_is_wrong_ = true;
return -1;
}
if (rule_json_.at("output").contains("error")) {
try {
error_str_ =
rule_json_.at("output").at("error").at("value").get<std::string>();
} catch (const std::exception &e) {
logger_->Error() << "output,error出错" << e.what()
<< ",location:" << BOOST_CURRENT_LOCATION << endl;
error_message_str_ += "报警内容格式异常!";
return -1;
}
}
res += this->reload_config_data_source(); /*3.数据源*/
res += this->first_fill_mm_vars(); /*4.数据项*/
res += init_X_exp();
rule_stat_.unit = unit_;
print_load_content();
for (auto tagid : tag_seq_) {
state_info_map_[tagid] = HoldState::StateInfo();
}
} catch (const std::exception &e) {
std::throw_with_nested(
mix_cc::Exception(-1, "load error", BOOST_CURRENT_LOCATION));
}
if (res == 0) {
this->exp_is_wrong_ = false;
}
return res;
}
Roller3::Roller3(const string &name, const mix_cc::json &rule_json,
const string &ruleId, size_t exp_type)
: ExpBase(name, rule_json, ruleId, exp_type) {
logger_.reset(
new LOG("Roller3_" + std::to_string(exp_type) + ":" + rule_name_,
AUTO_CATCH_PID));
}
Roller3::~Roller3() {
}
AlarmInfo Roller3::mon_proc() {
if ((bool)exp_act_->evaluate() == false) {
logger_->Debug() << "前提条件不满足!" << std::endl;
return AlarmInfo{};
}
/*最新数据*/
for (int i = 0; i < value_num_; i++) {
values_[i] = std::fabs(mm_vars["tag" + std::to_string(tag_seq_[i])]);
if (values_[i] == 0) {
is_zero_list_[i] = 1;
} else {
is_zero_list_[i] = 0;
}
}
/*中位数*/
median_ = calculateMedian(values_);
/*允许偏差*/
double deviationMax = std::fabs(median_ * limit_error_ * 0.01);
double deviationWarn = std::fabs(median_ * limit_warn_ * 0.01);
if (exp_type_ == ExpType::OuterAct) {
deviationMax = limit_error_;
deviationWarn = limit_warn_;
}
/*数据偏差*/
for (int j = 0; j < value_num_; j++) {
deviations_[j] = std::fabs(values_[j] - median_);
if (is_zero_list_[j] == 1) {
deviations_[j] = 0;
}
}
/*找最大偏差*/
auto maxIndexValue = findMaxWithIndex(deviations_);
limit_down_ = median_ - deviationWarn;
limit_up_ = median_ + deviationWarn;
rule_stat_.limit_down = get_up_down(limit_down_, 0);
rule_stat_.limit_up = get_up_down(limit_up_, 1);
rule_stat_.current_value =
std::fabs(mm_vars["tag" + std::to_string(maxIndexValue.first + 1)]);
/*无持续时间要求*/
if (hold_time_ <= this->delay_time_) {
/*报警检查*/
if (ExpBase::detect_up_down(rule_stat_.current_value)) {
std::string msg = "";
msg = error_str_ + "," +
SingletonTemplate<Item2Chines>::GetInstance()(
m_tags[maxIndexValue.first]) +
":" +
DAA::double2str(
std::fabs(
mm_vars["tag" + std::to_string(maxIndexValue.first + 1)]),
3) +
unit_ + ",合理区间:[" +
DAA::double2strLimit(rule_stat_.limit_down, 3) + "," +
DAA::double2strLimit(rule_stat_.limit_up, 3) + "]" + unit_;
auto alarm_time = get_alarm_time();
print_exp_vars();
logger_->Debug() << " median_:" << median_ << ",maxIndexValue:["
<< maxIndexValue.first << "," << maxIndexValue.second
<< "]," << msg << endl;
if (maxIndexValue.second > deviationMax) {
return utility::build_alarm_info("ERROR", rule_id_, rule_name_, "EXP16",
msg, alarm_time);
}
return utility::build_alarm_info("WARN", rule_id_, rule_name_, "EXP16",
msg, alarm_time);
}
} else {
std::string msg = "";
msg = error_str_;
HoldState::AlarmType alarm_type = HoldState::AlarmType::Warn;
/*持续时间检测*/
for (int i = 0; i < value_num_; i++) {
int tagid = tag_seq_[i];
double n_value = values_[i];
HoldState::AlarmState up_down = Roller3::detect_up_down(n_value);
HoldState::StateInfo &n_state_info = state_info_map_[tagid];
std::string tag_name;
int idx = tag_seq_[i] - 1;
if (idx >= 0 && idx < m_tags.size()) {
tag_name = SingletonTemplate<Item2Chines>::GetInstance()(m_tags[idx]);
}
HoldState::AlarmType n_alarm_type = deviations_[i] > deviationMax
? HoldState::AlarmType::Error
: HoldState::AlarmType::Warn;
if (is_zero_list_[i] != 1 && up_down != HoldState::AlarmState::None) {
if (up_down == n_state_info.last_alarm_tate) {
n_state_info.alarm_type = n_alarm_type == HoldState::AlarmType::Warn
? n_alarm_type
: n_state_info.alarm_type;
if ((now_time_ - n_state_info.last_start_time) > hold_time_) {
/*报警*/
msg = msg + "," + tag_name + ":" +
DAA::double2str(std::fabs(n_value), 3) + unit_;
alarm_type = n_state_info.alarm_type;
n_state_info.last_start_time = now_time_;
n_state_info.alarm_type = HoldState::AlarmType::Error;
}
} else {
n_state_info.last_start_time = now_time_;
}
}
n_state_info.last_alarm_tate = up_down;
}
if (!msg.empty()) {
auto alarm_time = get_alarm_time();
print_exp_vars();
logger_->Debug() << " median_:" << median_ << ",maxIndexValue:["
<< maxIndexValue.first << "," << maxIndexValue.second
<< "]," << msg << endl;
if (alarm_type == HoldState::AlarmType::Error) {
return utility::build_alarm_info("ERROR", rule_id_, rule_name_, "EXP16",
msg, alarm_time);
}
return utility::build_alarm_info("WARN", rule_id_, rule_name_, "EXP16",
msg, alarm_time);
}
}
return AlarmInfo{};
}
std::vector<AlarmInfo> Roller3::exec_task(mix_cc::time_range_t time_range) {
std::vector<AlarmInfo> result;
for (auto now_time = time_range.get_left();
now_time <= time_range.get_right(); now_time += delay_time_) {
this->now_time_ = now_time;
auto rr1 = this->exec_mon();
result.push_back(rr1);
}
return result;
}
void Roller3::print_load_content() {
logger_->Debug() << "limit_error_:" << limit_error_
<< ",exp_str_:" << exp_str_ << ",tags_exp_:" << tags_exp_
<< std::endl;
logger_->Debug() << "limit_error_:" << limit_error_
<< ",limit_warn_:" << limit_warn_ << ",单位unit_" << unit_
<< ",detect_mode_:" << detect_mode_
<< ",hold_time:" << hold_time_.count() << "ms" << std::endl;
}
int Roller3::init_X_exp() {
int res = 0;
if (rule_json_.at("function").contains("pre_exp")) {
string tmp_exp =
rule_json_.at("function").at("pre_exp").at("value").get<std::string>();
exp_str_ = get_macro_replaced_exp(tmp_exp);
res += init_hold_exp_str(exp_str_);
auto fun_res = fun_vars_.add_exp_str(exp_str_, &mm_vars);
res += fun_res.first ? 0 : -1;
exp_str_ = fun_res.second;
feedback_mode_ = false;
auto messy_code = exp_messy_code_check(exp_str_);
if (messy_code == -1) {
this->error_code_list_.push_back(
{ErrorType::EnCodeError, ErrorLocation::ActExp});
res += messy_code;
}
}
if (rule_json_.at("function").contains("input")) {
tags_exp_ =
rule_json_.at("function").at("input").at("value").get<std::string>();
auto tag_seq = extractTagNumbers(tags_exp_);
if (tag_seq.empty()) {
return -1;
}
std::sort(tag_seq.begin(), tag_seq.end());
tag_seq_ = tag_seq;
if (rule_json_.at("function")
.at("input")
.at("param")
.contains("limit_error")) {
limit_error_ = std::stod(rule_json_.at("function")
.at("input")
.at("param")
.at("limit_error")
.at("value")
.get<std::string>());
}
if (rule_json_.at("function")
.at("input")
.at("param")
.contains("limit_warn")) {
limit_warn_ = std::stod(rule_json_.at("function")
.at("input")
.at("param")
.at("limit_warn")
.at("value")
.get<std::string>());
}
if (rule_json_.at("function").at("input").at("param").contains("unit")) {
unit_ = rule_json_.at("function")
.at("input")
.at("param")
.at("unit")
.at("value")
.get<std::string>();
}
if (rule_json_.at("function")
.at("input")
.at("param")
.contains("detect_mode")) {
detect_mode_ = std::stoi(rule_json_.at("function")
.at("input")
.at("param")
.at("detect_mode")
.at("value")
.get<std::string>());
}
if (rule_json_.at("function")
.at("input")
.at("param")
.contains("hold_time")) {
hold_time_ = milliseconds(std::stoi(rule_json_.at("function")
.at("input")
.at("param")
.at("hold_time")
.at("value")
.get<std::string>()));
}
}
value_num_ = tag_seq_.size();
if (exp_act_ == nullptr && exp_str_ != "") {
try {
exp_act_ =
std::make_unique<mix_cc::matheval::Expression>(exp_str_, &mm_vars);
logger_->Debug() << "exp_act:" << exp_str_ << "=" << exp_act_->evaluate()
<< endl;
} catch (const std::exception &e) {
logger_->Error() << "exp_act:" << exp_str_ << "计算出错:" << e.what()
<< ",location:" << BOOST_CURRENT_LOCATION << endl;
this->error_code_list_.push_back(
{ErrorType::CalError, ErrorLocation::ActExp});
return -1;
}
}
if (exp_result_ == nullptr && tags_exp_ != "") {
try {
exp_result_ =
std::make_unique<mix_cc::matheval::Expression>(tags_exp_, &mm_vars);
logger_->Debug() << "tags_exp_:" << tags_exp_ << "="
<< exp_result_->evaluate() << endl;
} catch (const std::exception &e) {
logger_->Error() << "tags_exp_:" << tags_exp_ << "计算出错:" << e.what()
<< ",location:" << BOOST_CURRENT_LOCATION << endl;
this->error_code_list_.push_back(
{ErrorType::CalError, ErrorLocation::ActExp});
return -1;
}
}
deviations_.resize(value_num_, 0);
values_.resize(value_num_, 0);
is_zero_list_.resize(value_num_, 0);
return res;
}
vector<int> Roller3::extractTagNumbers(const string &expr) {
vector<int> result;
const string tagPrefix = "tag"; // 定义要查找的前缀
size_t pos = 0; // 当前查找位置
while (pos < expr.size()) {
// 查找下一个 "tag" 的出现位置
size_t found = expr.find(tagPrefix, pos);
if (found == string::npos) {
break; // 没有更多 "tag",退出循环
}
// 计算数字部分的起始位置(在 "tag" 之后)
size_t numStart = found + tagPrefix.size();
size_t numEnd = numStart;
// 提取连续的数字字符序列(直到遇到非数字字符,如 '+' 或字符串结尾)
while (numEnd < expr.size() && isdigit(expr[numEnd])) {
numEnd++;
}
// 如果存在数字序列,则转换为整数并添加到结果中
if (numEnd > numStart) {
string numStr = expr.substr(numStart, numEnd - numStart);
try {
int num = stoi(numStr);
result.push_back(num);
} catch (const invalid_argument &e) {
// 处理无效数字字符串(例如非数字内容),根据需求可忽略或记录错误
// 这里选择跳过无效转换,继续处理后续部分
} catch (const out_of_range &e) {
// 处理数字超出 int 范围的情况,同样跳过
}
}
// 更新查找位置到当前数字序列的末尾,继续搜索
pos = numEnd;
}
return result;
}
double Roller3::calculateMedian(
std::vector<double> data) { // 传值以创建副本,不改变原向量
if (data.empty())
return 0.0;
size_t size = data.size();
std::sort(data.begin(), data.end());
if (size % 2 == 0) {
// 偶数个元素,取中间两个数的平均值
return (data[size / 2 - 1] + data[size / 2]) / 2.0;
} else {
// 奇数个元素,取中间的数
return data[size / 2];
}
}
// 函数返回一个pairfirst是下标second是最大值
std::pair<int, double>
Roller3::findMaxWithIndex(const std::vector<double> &vec) {
// 检查vector是否为空
if (vec.empty()) {
return std::make_pair(-1, 0.0); // 返回无效值
}
// 使用max_element找到最大元素的迭代器
auto max_iter = std::max_element(vec.begin(), vec.end());
int index = std::distance(vec.begin(), max_iter);
// 获取最大值
double max_value = *max_iter;
return std::make_pair(index, max_value);
}
double Roller3::get_up_down(const double &value, bool is_up) {
switch (this->detect_mode_) {
case DetectMode::Default:
return value;
break;
case DetectMode::OnlyLeft:
return is_up ? 32767 : value;
break;
case DetectMode::OnlyRight:
return is_up ? value : -32768;
break;
default:
return value;
break;
}
return value;
}
HoldState::AlarmState Roller3::detect_up_down(const double &value) {
switch (this->detect_mode_) {
case DetectMode::Default: {
if (value < this->limit_down_) {
return HoldState::AlarmState::DownLower;
} else if (value > limit_up_) {
return HoldState::AlarmState::UpHigher;
} else {
return HoldState::AlarmState::None;
}
} break;
case DetectMode::OnlyLeft:
return value < limit_down_ ? HoldState::AlarmState::DownLower
: HoldState::AlarmState::None;
break;
case DetectMode::OnlyRight:
return value > limit_up_ ? HoldState::AlarmState::UpHigher
: HoldState::AlarmState::None;
break;
default:
return HoldState::AlarmState::None;
break;
}
return HoldState::AlarmState::None;
}