463 lines
16 KiB
C++
463 lines
16 KiB
C++
|
||
#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() {
|
||
// AlgBase::~AlgBase();
|
||
}
|
||
|
||
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 =
|
||
// mm_vars["tag" + std::to_string(maxIndexValue.first + 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); // 将字符串转换为整数[7](@ref)
|
||
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];
|
||
}
|
||
}
|
||
|
||
// 函数返回一个pair,first是下标,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找到最大元素的迭代器[1,2,5](@ref)
|
||
auto max_iter = std::max_element(vec.begin(), vec.end());
|
||
|
||
// 计算下标序号[1,6,8](@ref)
|
||
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;
|
||
} |