eis/eqpalg/algs/roller3.cpp

462 lines
15 KiB
C++
Raw Normal View History

#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();
2026-05-09 13:33:07 +08:00
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;
}