/****************************************************************************************************************** * Action instruction algorithm *表达式反馈算法 * arg[0] action expression * arg[1] feedback expression * arg[2] expression of judgment result * * 1.0 2020-12-17 zoufuzhou ******************************************************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include Exp::Exp(const string& name, const mix_cc::json& rule_json, const string& ruleId, size_t dims) : AlgBase(name, rule_json, ruleId), dims_(dims) { logger_.reset(new LOG("Exp:" + rule_name_, AUTO_CATCH_PID)); } Exp::~Exp() { // utility::unlock_file(rule_id_); } int Exp::init() { int ret = 0; try { ret += AlgBase::init(); /*1.tag点;2.执行周期*/ // 重新载入数据源配置信息 ret += this->reload_config_data_source(); /*3.数据源*/ // 在载入数据源信息完成后,载入表达式配置之前,必须刷新变量,把变量信息初始化到mm_vars内 ret += this->first_fill_mm_vars(); /*4.数据项*/ // 必须在刷新变量后,才可以初始化表达式 ret += this->reload_config_exp_act(); /*5.动作表达式*/ if (feedback_mode_) { ret += this->reload_config_exp_feedback(); /*6.反馈表达式*/ } } catch (const std::exception& e) { std::throw_with_nested( mix_cc::Exception(-1, "load error", BOOST_CURRENT_LOCATION)); } return ret; } AlarmInfo Exp::exec_mon() { AlarmInfo out_alarm{}; try { this->refresh_now_time(); // 根据数据来源种类,决定最后的执行过程 switch (data_source_) { // 如果是共享内存,只需要执行当前周期的数据和表达式 // 并判断是否报警 case DataSource::MEMORY: { refresh_exp_vars_mem(); if (this->refresh_counts_ < 3) { this->refresh_counts_++; } else { out_alarm = mon_proc(); } } break; // 如果是ihyperDB case DataSource::IHDB: // 需要先重新载入ihyperDB的缓存数据 refresh_ihd_cache(); // 然后刷以此把缓存中的数据取出 for (auto i = 0; i < queried_data_.rows(); i++) { refresh_exp_vars_ihd(i); if (!out_alarm.alarmed) { auto tmp = mon_proc(); if (tmp.alarmed) { out_alarm = (tmp); } } } break; default: break; } } catch (const std::exception& e) { gb_logger_->log_exception(e); } return out_alarm; } // 单次执行 std::vector Exp::exec_task(mix_cc::time_range_t time_range) { std::vector out_alarms; try { // 刷一遍 mmvar 防止p1_3数据错误带来的问题 now_time_ = time_range.get_left() - query_interval_time_; if (this->delay_time_ > this->ihd_min_time_particles_ && (this->delay_time_.count() % this->ihd_min_time_particles_.count() == 0)) { refresh_ihd_cache(this->delay_time_); } else { refresh_ihd_cache(); } for (auto i = 0; i < queried_data_.rows(); i++) { refresh_exp_vars_ihd(i); } // 对每个ihdb 查询周期的数据,进行处理 for (auto now_time = time_range.get_left(); now_time <= time_range.get_right(); now_time += query_interval_time_) { this->now_time_ = now_time; if (this->delay_time_ > this->ihd_min_time_particles_ && (this->delay_time_.count() % this->ihd_min_time_particles_.count() == 0)) { refresh_ihd_cache(this->delay_time_); } else { refresh_ihd_cache(); } for (auto i = 0; i < queried_data_.rows(); i++) { refresh_exp_vars_ihd(i); // gb_logger_->log_info(this->rule_name_+":测试"); auto tmp = mon_proc(); if (tmp.alarmed) { // gb_logger_->log_info(this->rule_name_+"满足报警"); out_alarms.push_back(tmp); } } } // utility::unlock_file(rule_id_); gb_logger_->log_info("本次测试报警数量:" + std::to_string(out_alarms.size())); auto msg = rule_name_ + "本次测试报警数量:" + std::to_string(out_alarms.size()); auto alarm_task = utility::build_alarm_info( MsgLevel::INFO, rule_id_, rule_name_, "EXPACT", msg, time_range); out_alarms.clear(); out_alarms.push_back(alarm_task); } catch (const std::exception& e) { gb_logger_->log_exception(e); } return out_alarms; } AlarmInfo Exp::mon_proc() { // 监控基本过程 double result_value; try { // 获得是否满足前提条件表达式 act_triggered_ = static_cast(exp_act_->evaluate()); // 如果是反馈模式 if (feedback_mode_) { // 获得是否满足反馈条件表达式 feedback_triggered_ = static_cast(exp_feedback_->evaluate()); // 如果 if (act_start_done()) { // print_exp_vars(); return AlarmInfo{}; } if (act_not_hold()) { return AlarmInfo{}; } if (act_done()) { result_value = exp_result_->evaluate(); print_exp_vars(); logger_->Debug() << " action end:" << mix_cc::mix_time_t(query_time_range_.get_right()) .to_formatted_time() << " timediff:" << mm_vars["time"] << " exp:" << exp_str_ << "=" << result_value << endl; if (result_value) { std::string msg_tag = ""; if (exp_str_ == "isEntOK" || exp_str_ == "isCentOK" || exp_str_ == "isExitOK") { for (int i = 0; i < m_tags.size(); i++) { msg_tag += "tag" + std::to_string(i + 1) + ":" + std::to_string(mm_vars["tag" + std::to_string(i + 1)]) + " "; } } if (exp_str_.find("mx_tag") != std::string::npos) { for (int i = 0; i < m_tags.size(); i++) { // msg_tag += // "max(tag" + std::to_string(i + 1) + "):" + // std::to_string(mm_vars["mx_tag" + std::to_string(i + 1)]) + // " " + "min(tag" + std::to_string(i + 1) + "):" + // std::to_string(mm_vars["mi_tag" + std::to_string(i + 1)]) + // " "; msg_tag += std::to_string(mm_vars["mx_tag" + std::to_string(i + 1)] - mm_vars["mi_tag" + std::to_string(i + 1)]) + " "; } } auto msg = rule_name_ + " " + error_str_ + msg_tag; logger_->Debug() << msg << endl; return utility::build_alarm_info(MsgLevel::ERROR, rule_id_, rule_name_, "EXPACT", msg, query_time_range_); } } else if (act_timeout()) { return this->get_timeout_alarm(); } } // 不是动作反馈,则act_triggered_ 即为报警触发条件 2021-10-27 else { if (act_triggered_) { print_exp_vars(); auto msg = rule_name_ + " " + error_str_; logger_->Debug() << msg << endl; this->query_time_range_.set_left(query_time_range_.get_right() - delay_time_); // return utility::build_alarm_info(MsgLevel::ERROR, rule_id_, rule_name_, "EXP", msg, query_time_range_); } } } catch (const std::exception& e) { std::throw_with_nested( mix_cc::Exception(-1, "mon_proc error", BOOST_CURRENT_LOCATION)); } return AlarmInfo{}; } mix_cc::json Exp::exec_cron() { return {}; } bool Exp::act_start_done() { // 如果动作未开始且前提条件满足 if (!act_started_ && act_triggered_) { // 则认为动作开始,并置动作开始时间为当前时间 act_started_ = true; act_start_time_ = this->now_time_; for (unsigned int i = 0; i < m_tags.size(); i++) { // s[n] 表示tag[n]在动作开始时刻的起始值 mm_vars["s" + std::to_string(i + 1)] = mm_vars["tag" + std::to_string(i + 1)]; mm_vars["stime"] = duration_cast(now_time_.time_since_epoch()).count(); // mv2 变量 mm_vars["mx_tag" + std::to_string(i + 1)] = mm_vars["tag" + std::to_string(i + 1)]; mm_vars["mi_tag" + std::to_string(i + 1)] = mm_vars["tag" + std::to_string(i + 1)]; // mm_vars["mx_p" + std::to_string(i + 1)] = // mm_vars["p" + std::to_string(i + 1)]; // mm_vars["mi_p" + std::to_string(i + 1)] = // mm_vars["p" + std::to_string(i + 1)]; mm_vars["mv2_tag" + std::to_string(i + 1)] = 0; mm_vars["mv2_p" + std::to_string(i + 1)] = 0; if (mm_vars["tag" + std::to_string(i + 1)] == 1) { mm_vars["mv2_tag" + std::to_string(i + 1)] = 1; } if (mm_vars["p" + std::to_string(i + 1)] == 1) { mm_vars["mv2_p" + std::to_string(i + 1)] = 1; } } // 重设query time range 的 left,使得时间下限为报警的时间开始 this->query_time_range_.set_left(now_time_); // 检查动作开时间条件是否错误 logger_->Debug() << " action start:" << mix_cc::mix_time_t(act_start_time_).to_formatted_time() << endl; return true; } return false; } AlarmInfo Exp::cron_proc() { return AlarmInfo{}; } bool Exp::act_not_hold() { // 如果动作开始,且需要保持,且动作开始没有被触发, if (act_started_ && keep_mode_ && !act_triggered_) { // 开始条件act_started置为假,即动作停止 act_started_ = false; logger_->Debug() << " action signal is not holding " << endl; return true; } return false; } bool Exp::act_done() { // 如果动作已开始 刷新 mv2_tag[n] if (act_started_) { for (unsigned int i = 0; i < m_tags.size(); i++) { mm_vars["mx_tag" + std::to_string(i + 1)] = std::max(mm_vars["tag" + std::to_string(i + 1)], mm_vars["mx_tag" + std::to_string(i + 1)]); mm_vars["mi_tag" + std::to_string(i + 1)] = std::min(mm_vars["tag" + std::to_string(i + 1)], mm_vars["mi_tag" + std::to_string(i + 1)]); // mm_vars["mx_p" + std::to_string(i + 1)] = // std::max(mm_vars["p" + std::to_string(i + 1)], // mm_vars["mx_p" + std::to_string(i + 1)]); // mm_vars["mi_p" + std::to_string(i + 1)] = // std::min(mm_vars["p" + std::to_string(i + 1)], // mm_vars["mi_p" + std::to_string(i + 1)]); if (mm_vars["tag" + std::to_string(i + 1)] == 1) { mm_vars["mv2_tag" + std::to_string(i + 1)] += 1; } if (mm_vars["p" + std::to_string(i + 1)] == 1) { mm_vars["mv2_p" + std::to_string(i + 1)] += 1; } } } // 如果动作处于开始状态,且反馈模式触发 if (act_started_ && feedback_triggered_) { // 则记录下结束时间 mm_vars["etime"] = mm_vars["now"]; mm_vars["time"] = mm_vars["etime"] - mm_vars["stime"]; act_started_ = false; return true; } return false; } // 表达式系统触发-反馈动作超时 bool Exp::act_timeout() { if (time_out_ == milliseconds(0)) { // 重置mv_tag 防止崩溃 for (unsigned int i = 0; i < m_tags.size(); i++) { if (abs(mm_vars["mv2_tag" + std::to_string(i + 1)] - DBL_MAX) < 2.0) { mm_vars["mv2_tag" + std::to_string(i + 1)] = 0; mm_vars["mv2_p" + std::to_string(i + 1)] = 0; this->logger_->Debug() << "mv2_tag" + std::to_string(i + 1) << "已达上限,被清空" << endl; act_started_ = false; return true; //不再继续 } } return false; } // 如果当动作处于开始状态 当前时间减去开始时间大于超时时间,则认为超时 if (act_started_ && (now_time_ - act_start_time_) > time_out_) { act_started_ = false; // 超时 重置 防止崩溃 logger_->Debug() << "动作反馈超时,mv2_tag将被重置" << endl; for (unsigned int i = 0; i < m_tags.size(); i++) { logger_->Debug() << "当前mv2_tag" << i + 1 << "=" << mm_vars["mv2_tag" + std::to_string(i + 1)] << "s" << endl; mm_vars["mv2_tag" + std::to_string(i + 1)] = 0; mm_vars["mv2_p" + std::to_string(i + 1)] = 0; } return true; } return false; } // 得到报警超时信息 AlarmInfo Exp::get_timeout_alarm() { auto msg = rule_name_ + " no feedback on the action"; logger_->Debug() << msg << endl; return utility::build_alarm_info(MsgLevel::ERROR, rule_id_, rule_name_, "EXPACT", msg, query_time_range_); } int Exp::reload_config_exp_feedback() { // 获取feedback信息 if (rule_json_.at("action_condition").contains("action_hold")) { // 判断是否保持 keep_mode_ = rule_json_.at("action_condition").at("action_hold").at(1).get(); logger_->Debug() << "keep:" << keep_mode_ << endl; time_out_ = milliseconds( rule_json_.at("interval").at("timeout").at(1).get()); // 如果超时时间小于3分钟,则默认为超时时间为3分钟 if (time_out_ < minutes(3) && time_out_ != milliseconds(0)) { time_out_ = minutes(3); } // 获取feedback表达式 logger_->Debug() << "timeout:" << time_out_.count() << endl; auto tmp_exp = string(rule_json_.at("action_condition") .at("action_end") .at(1) .get()); exp_str_ = get_macro_replaced_exp(tmp_exp); // 构建feedback表达式 // exp_feedback_ = // std::make_unique(exp_str_, &mm_vars); // logger_->Debug() << exp_str_ << ":" << exp_feedback_->evaluate() << endl; // 2022-1-18 测试 try { exp_feedback_ = std::make_unique(exp_str_, &mm_vars); logger_->Debug() << exp_str_ << ":" << exp_feedback_->evaluate() << endl; } catch (const std::exception& e) { logger_->Error() << exp_str_ << "计算错误:" << e.what() << endl; } } else { time_out_ = milliseconds(600000); } // 获取监控变量信息 auto tmp_exp = rule_json_.at("alarm_option").at("value").at(1).get(); exp_str_ = get_macro_replaced_exp(tmp_exp); // 获取 error_str_ = rule_json_.at("alarm_option").at("error").at(1).get(); // 如果结果表达式没有初始化,则初始化之后再次使用 try { if (exp_result_ == nullptr) { exp_result_ = std::make_unique(exp_str_, &mm_vars); logger_->Debug() << exp_str_ << ":" << exp_result_->evaluate() << endl; } else { logger_->Info() << "指针已经初始化完成" << exp_str_ << ":" << exp_result_->evaluate() << endl; } } catch (const std::exception& e) { logger_->Error() << exp_result_ << "计算错误:" << e.what() << endl; } // if (exp_result_ == nullptr) { // exp_result_ = // std::make_unique(exp_str_, &mm_vars); // logger_->Debug() << exp_str_ << ":" << exp_result_->evaluate() << endl; // } else { // logger_->Info() << "指针已经初始化完成" << exp_str_ << ":" // << exp_result_->evaluate() << endl; // } if (exp_str_.find("time", 0) != string::npos) { m_timemode = true; } else { m_timemode = false; } return 0; } // 刷新共享内存所对应的变量 int Exp::refresh_exp_vars_mem() { refresh_now_time(); for (unsigned int i = 0; i < m_tags.size(); i++) { // s[n] 表示tag[n]在动作开始时刻的起始值 // p[n] 表示tag[n]在上一个动作周期的数值 mm_vars["p" + std::to_string(i + 1)] = mm_vars["tag" + std::to_string(i + 1)]; mm_vars["tag" + std::to_string(i + 1)] = SingletonTemplate::GetInstance()[m_tags[i]]; mm_vars["now"] = duration_cast(now_time_.time_since_epoch()).count(); auto pv_str = "pv" + std::to_string(i + 1); mm_vars[pv_str + "_4"] = mm_vars[pv_str + "_3"]; mm_vars[pv_str + "_3"] = mm_vars[pv_str + "_2"]; mm_vars[pv_str + "_2"] = mm_vars[pv_str + "_1"]; mm_vars[pv_str + "_1"] = mm_vars[pv_str + "_0"]; mm_vars[pv_str + "_0"] = mm_vars["tag" + std::to_string(i + 1)]; } mm_vars["isEntOK"] = SingletonTemplate::GetInstance().isEntOK(); mm_vars["isCentOK"] = SingletonTemplate::GetInstance().isCentOK(); mm_vars["isExitOK"] = SingletonTemplate::GetInstance().isExitOK(); // 设置right的时间 this->query_time_range_.set_right(now_time_); return 0; } // 重新载入数据源配置 int Exp::reload_config_data_source() { int res = 0; if (!rule_json_.contains("datasource")) { data_source_ = 0; logger_->Debug() << "默认数据源为iHyerDB! data source[0:iHyerDB,1:memory]:" << data_source_ << endl; return 0; } try { data_source_ = std::stoi(rule_json_.at("datasource").at("value").get()); logger_->Debug() << "data source[0:iHyerDB,1:memory]:" << data_source_ << endl; } catch (const std::exception& e) { gb_logger_->log_error(std::string("ExpBase::reload_config_data_source()") + e.what()); this->error_code_list_.push_back( {ErrorType::Empty, ErrorLocation::DataSource}); return -1; } return 0; } // 刷新对应ihd cache内第row行的数据变量 int Exp::refresh_exp_vars_ihd(int row) { // i <= row 原本查询时间对应的是[t1,t2) // 则queried_time_内部的时间也一定是[t1,t2) for (unsigned int i = 0; i < m_tags.size(); i++) { mm_vars["p" + std::to_string(i + 1)] = mm_vars["tag" + std::to_string(i + 1)]; mm_vars["tag" + std::to_string(i + 1)] = queried_data_(row, i); auto pv_str = "pv" + std::to_string(i + 1); mm_vars[pv_str + "_4"] = mm_vars[pv_str + "_3"]; mm_vars[pv_str + "_3"] = mm_vars[pv_str + "_2"]; mm_vars[pv_str + "_2"] = mm_vars[pv_str + "_1"]; mm_vars[pv_str + "_1"] = mm_vars[pv_str + "_0"]; mm_vars[pv_str + "_0"] = mm_vars["tag" + std::to_string(i + 1)]; // mm_vars["now"] = mix_cc::mix_time_t(queried_time_[row]).to_milliseconds(); // right一定小于t2 this->query_time_range_.set_right(queried_time_[row]); // 当前时间 == 上次执行的结束时间 this->now_time_ = queried_time_[row]; } mm_vars["isEntOK"] = SingletonTemplate::GetInstance().isEntOK_ihd(); mm_vars["isCentOK"] = SingletonTemplate::GetInstance().isCentOK_ihd(); mm_vars["isExitOK"] = SingletonTemplate::GetInstance().isExitOK_ihd(); return 0; } // 程序启动时刻的,首次刷新变量数据 // 防止程序因为p1 pv1类似的变量,导致程序的变量差值信息错误 int Exp::first_fill_mm_vars() { if (data_source_ == DataSource::MEMORY) { mm_vars["isEntOK"] = false; mm_vars["isCentOK"] = false; mm_vars["isExitOK"] = false; for (unsigned int i = 0; i < m_tags.size(); i++) { auto pv_str = "pv" + std::to_string(i + 1); auto value = SingletonTemplate::GetInstance()[m_tags[i]]; mm_vars["s" + std::to_string(i + 1)] = value; mm_vars["p" + std::to_string(i + 1)] = value; mm_vars["tag" + std::to_string(i + 1)] = value; mm_vars["mv2_tag" + std::to_string(i + 1)] = 0; mm_vars["mv2_p" + std::to_string(i + 1)] = 0; mm_vars["mx_tag" + std::to_string(i + 1)] = mm_vars["tag" + std::to_string(i + 1)]; mm_vars["mi_tag" + std::to_string(i + 1)] = mm_vars["tag" + std::to_string(i + 1)]; // mm_vars["mx_p" + std::to_string(i + 1)] = // mm_vars["p" + std::to_string(i + 1)]; // mm_vars["mi_p" + std::to_string(i + 1)] = // mm_vars["p" + std::to_string(i + 1)]; mm_vars[pv_str + "_0"] = value; mm_vars[pv_str + "_1"] = value; mm_vars[pv_str + "_2"] = value; mm_vars[pv_str + "_3"] = value; mm_vars[pv_str + "_4"] = value; mm_vars["stime"] = 0; mm_vars["now"] = 0; mm_vars["etime"] = 0; mm_vars["time"] = 0; } } else if (data_source_ == DataSource::IHDB) { this->refresh_now_time(); this->refresh_ihd_cache(); if (queried_data_.rows() == 0) { logger_->Error() << "first_fill_mm_vars(),IHDB查询异常,未查到数据!" << endl; this->error_code_list_.push_back( {ErrorType::Empty, ErrorLocation::DataValue}); return -1; } mm_vars["isEntOK"] = false; mm_vars["isCentOK"] = false; mm_vars["isExitOK"] = false; for (unsigned int i = 0; i < m_tags.size(); i++) { auto pv_str = "pv" + std::to_string(i + 1); auto rows = queried_data_.rows() - 1; auto tmp_val = queried_data_(rows, i); mm_vars["s" + std::to_string(i + 1)] = tmp_val; mm_vars["p" + std::to_string(i + 1)] = tmp_val; mm_vars["tag" + std::to_string(i + 1)] = tmp_val; mm_vars["mv2_tag" + std::to_string(i + 1)] = 0; mm_vars["mv2_p" + std::to_string(i + 1)] = 0; mm_vars["mx_tag" + std::to_string(i + 1)] = mm_vars["tag" + std::to_string(i + 1)]; mm_vars["mi_tag" + std::to_string(i + 1)] = mm_vars["tag" + std::to_string(i + 1)]; // mm_vars["mx_p" + std::to_string(i + 1)] = // mm_vars["p" + std::to_string(i + 1)]; // mm_vars["mi_p" + std::to_string(i + 1)] = // mm_vars["p" + std::to_string(i + 1)]; mm_vars[pv_str + "_0"] = tmp_val; mm_vars[pv_str + "_1"] = tmp_val; mm_vars[pv_str + "_2"] = tmp_val; mm_vars[pv_str + "_3"] = tmp_val; mm_vars[pv_str + "_4"] = tmp_val; mm_vars["stime"] = 0; mm_vars["now"] = 0; mm_vars["etime"] = 0; mm_vars["time"] = 0; } } return 0; } int Exp::reload_config_exp_act() { // 根据key,对不同版本的算法都进行取值 if (rule_json_.at("action_condition").contains("action_start")) { auto tmp_exp = rule_json_.at("action_condition") .at("action_start") .at(1) .get(); exp_str_ = get_macro_replaced_exp(tmp_exp); feedback_mode_ = true; } else if (rule_json_.at("action_condition").contains("value")) { auto tmp_exp = rule_json_.at("action_condition").at("value").at(1).get(); exp_str_ = get_macro_replaced_exp(tmp_exp); if (rule_json_.at("action_condition").contains("error")) { error_str_ = rule_json_.at("action_condition") .at("error") .at(1) .get(); } feedback_mode_ = false; } if (exp_act_ == nullptr && exp_str_ != "") { // exp_act_ = // std::make_unique(exp_str_, &mm_vars); // logger_->Debug() << "exp_act:" << exp_str_ << "=" << exp_act_->evaluate() // << endl; // 2022-1-18 测试 try { exp_act_ = std::make_unique(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() << endl; } } return 0; } // 打印表达式信息 void Exp::print_exp_vars(const string& expstr) { std::map::iterator it; logger_->Debug() << "exp:" << expstr << " parameter:"; for (it = mm_vars.begin(); it != mm_vars.end(); it++) { logger_->Debug() << " " << it->first << ":" << it->second; } logger_->Debug() << endl; } void Exp::set_last_alarm_time(TimePoint time_point) { this->refresh_counts_ = 0; if (this->is_usable_) { this->first_fill_mm_vars(); ///<打破原动作状态 } AlgBase::set_last_alarm_time(time_point); } void Exp::set_usable(bool usable) { this->refresh_counts_ = 0; if (this->is_usable_) { this->first_fill_mm_vars(); ///<打破原动作状态 } AlgBase::set_usable(usable); }