mirror of
https://github.com/MaaAssistantArknights/MaaAssistantArknights.git
synced 2026-07-16 01:40:46 +08:00
647 lines
23 KiB
C++
647 lines
23 KiB
C++
#include "InfrastProductionTask.h"
|
||
|
||
#include <algorithm>
|
||
|
||
#include <calculator/calculator.hpp>
|
||
|
||
#include "Controller.h"
|
||
#include "InfrastOperImageAnalyzer.h"
|
||
#include "HashImageAnalyzer.h"
|
||
#include "Logger.hpp"
|
||
#include "MatchImageAnalyzer.h"
|
||
#include "MultiMatchImageAnalyzer.h"
|
||
#include "Resource.h"
|
||
#include "RuntimeStatus.h"
|
||
#include "ProcessTask.h"
|
||
|
||
asst::InfrastProductionTask& asst::InfrastProductionTask::set_uses_of_drone(std::string uses_of_drones) noexcept
|
||
{
|
||
m_uses_of_drones = std::move(uses_of_drones);
|
||
return *this;
|
||
}
|
||
|
||
std::string asst::InfrastProductionTask::get_uses_of_drone() const noexcept
|
||
{
|
||
return m_uses_of_drones;
|
||
}
|
||
|
||
void asst::InfrastProductionTask::set_product(std::string product_name) noexcept
|
||
{
|
||
m_product = std::move(product_name);
|
||
|
||
json::value callback_info = basic_info_with_what("ProductOfFacility");
|
||
callback_info["details"]["product"] = m_product;
|
||
callback(AsstMsg::SubTaskExtraInfo, callback_info);
|
||
}
|
||
|
||
bool asst::InfrastProductionTask::shift_facility_list()
|
||
{
|
||
LogTraceFunction;
|
||
if (!facility_list_detect()) {
|
||
return false;
|
||
}
|
||
if (need_exit()) {
|
||
return false;
|
||
}
|
||
const auto tab_task_ptr = Task.get("InfrastFacilityListTab" + facility_name());
|
||
MatchImageAnalyzer add_analyzer;
|
||
const auto add_task_ptr = Task.get("InfrastAddOperator" + facility_name() + m_work_mode_name);
|
||
add_analyzer.set_task_info(add_task_ptr);
|
||
MultiMatchImageAnalyzer locked_analyzer;
|
||
locked_analyzer.set_task_info("InfrastOperLocked" + facility_name());
|
||
|
||
for (; m_cur_facility_index < m_facility_list_tabs.size(); ++m_cur_facility_index) {
|
||
Rect tab = m_facility_list_tabs.at(m_cur_facility_index);
|
||
if (need_exit()) {
|
||
return false;
|
||
}
|
||
if (m_cur_facility_index != 0) {
|
||
callback(AsstMsg::SubTaskExtraInfo, basic_info_with_what("EnterFacility"));
|
||
}
|
||
|
||
Ctrler.click(tab);
|
||
sleep(tab_task_ptr->rear_delay);
|
||
|
||
/* 识别当前制造/贸易站有没有添加干员按钮,没有就不换班 */
|
||
const auto image = Ctrler.get_image();
|
||
add_analyzer.set_image(image);
|
||
if (!add_analyzer.analyze()) {
|
||
Log.info("no add button, just continue");
|
||
continue;
|
||
}
|
||
auto& rect = add_analyzer.get_result().rect;
|
||
Rect add_button = rect;
|
||
auto& rect_move = add_task_ptr->rect_move;
|
||
if (!rect_move.empty()) {
|
||
add_button.x += rect_move.x;
|
||
add_button.y += rect_move.y;
|
||
add_button.width = rect_move.width;
|
||
add_button.height = rect_move.height;
|
||
}
|
||
|
||
/* 识别当前正在造什么 */
|
||
MatchImageAnalyzer product_analyzer(image);
|
||
auto& all_products = Resrc.infrast().get_facility_info(facility_name()).products;
|
||
std::string cur_product = all_products.at(0);
|
||
double max_score = 0;
|
||
for (const std::string& product : all_products) {
|
||
product_analyzer.set_task_info("InfrastFlag" + product);
|
||
if (product_analyzer.analyze()) {
|
||
double score = product_analyzer.get_result().score;
|
||
if (score > max_score) {
|
||
max_score = score;
|
||
cur_product = product;
|
||
}
|
||
}
|
||
}
|
||
set_product(cur_product);
|
||
|
||
locked_analyzer.set_image(image);
|
||
if (locked_analyzer.analyze()) {
|
||
m_cur_num_of_lokced_opers = static_cast<int>(locked_analyzer.get_result().size());
|
||
}
|
||
else {
|
||
m_cur_num_of_lokced_opers = 0;
|
||
}
|
||
|
||
/* 进入干员选择页面 */
|
||
Ctrler.click(add_button);
|
||
sleep(add_task_ptr->rear_delay);
|
||
|
||
for (int i = 0; i <= OperSelectRetryTimes; ++i) {
|
||
if (need_exit()) {
|
||
return false;
|
||
}
|
||
click_clear_button();
|
||
|
||
if (m_all_available_opers.empty()) {
|
||
if (!opers_detect_with_swipe()) {
|
||
return false;
|
||
}
|
||
swipe_to_the_left_of_operlist(2);
|
||
}
|
||
else {
|
||
opers_detect();
|
||
}
|
||
optimal_calc();
|
||
bool ret = opers_choose();
|
||
if (!ret) {
|
||
m_all_available_opers.clear();
|
||
swipe_to_the_left_of_operlist(2);
|
||
continue;
|
||
}
|
||
break;
|
||
}
|
||
click_confirm_button();
|
||
|
||
// 使用无人机
|
||
if (cur_product == m_uses_of_drones) {
|
||
if (use_drone()) {
|
||
m_uses_of_drones = "_Used";
|
||
}
|
||
}
|
||
}
|
||
return true;
|
||
}
|
||
|
||
bool asst::InfrastProductionTask::opers_detect_with_swipe()
|
||
{
|
||
LogTraceFunction;
|
||
m_all_available_opers.clear();
|
||
|
||
while (true) {
|
||
if (need_exit()) {
|
||
return false;
|
||
}
|
||
size_t num = opers_detect();
|
||
Log.trace("opers_detect return", num);
|
||
|
||
// 无论如何也不会没有干员的,除非前面的操作哪里出错了,没进到干员选择的页面。那也就没必要继续下去了
|
||
if (num == 0) {
|
||
return false;
|
||
}
|
||
|
||
// 这里本来是判断不相等就可以退出循环。
|
||
// 但是有时候滑动会把一个干员挡住一半,一个页面完整的干员真的只有10个,所以加个2的差值
|
||
if (max_num_of_opers_per_page - num > 2) {
|
||
break;
|
||
}
|
||
|
||
// 异步在最后会多滑动一下,耽误的时间还不如用同步
|
||
swipe_of_operlist();
|
||
}
|
||
|
||
if (!m_all_available_opers.empty()) {
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
|
||
size_t asst::InfrastProductionTask::opers_detect()
|
||
{
|
||
LogTraceFunction;
|
||
const auto image = Ctrler.get_image();
|
||
|
||
InfrastOperImageAnalyzer oper_analyzer(image);
|
||
oper_analyzer.set_facility(facility_name());
|
||
|
||
if (!oper_analyzer.analyze()) {
|
||
return 0;
|
||
}
|
||
const auto& cur_all_opers = oper_analyzer.get_result();
|
||
max_num_of_opers_per_page = (std::max)(max_num_of_opers_per_page, cur_all_opers.size());
|
||
|
||
const int face_hash_thres = std::dynamic_pointer_cast<HashTaskInfo>(
|
||
Task.get("InfrastOperFaceHash"))->dist_threshold;
|
||
int cur_available_num = static_cast<int>(cur_all_opers.size());
|
||
for (const auto& cur_oper : cur_all_opers) {
|
||
if (cur_oper.skills.empty()) {
|
||
--cur_available_num;
|
||
continue;
|
||
}
|
||
{
|
||
std::string skills_str = "[";
|
||
for (const auto& skill : cur_oper.skills) {
|
||
skills_str += skill.id + ", ";
|
||
}
|
||
skills_str += "]";
|
||
Log.trace(skills_str, "mood", cur_oper.mood_ratio, "threshold", m_mood_threshold);
|
||
}
|
||
// 心情过低的干员则不可用
|
||
if (cur_oper.mood_ratio < m_mood_threshold) {
|
||
//--cur_available_num;
|
||
continue;
|
||
}
|
||
auto find_iter = std::find_if(
|
||
m_all_available_opers.cbegin(), m_all_available_opers.cend(),
|
||
[&](const infrast::Oper& oper) -> bool {
|
||
// 有可能是同一个干员,比一下hash
|
||
int dist = HashImageAnalyzer::hamming(cur_oper.face_hash, oper.face_hash);
|
||
Log.debug("opers_detect hash dist |", dist);
|
||
return dist < face_hash_thres;
|
||
});
|
||
// 如果两个的hash距离过小,则认为是同一个干员,不进行插入
|
||
if (find_iter != m_all_available_opers.cend()) {
|
||
continue;
|
||
}
|
||
m_all_available_opers.emplace_back(cur_oper);
|
||
}
|
||
return cur_available_num;
|
||
}
|
||
|
||
bool asst::InfrastProductionTask::optimal_calc()
|
||
{
|
||
LogTraceFunction;
|
||
auto& facility_info = Resrc.infrast().get_facility_info(facility_name());
|
||
int cur_max_num_of_opers = facility_info.max_num_of_opers - m_cur_num_of_lokced_opers;
|
||
|
||
std::vector<infrast::SkillsComb> all_avaliable_combs;
|
||
all_avaliable_combs.reserve(m_all_available_opers.size());
|
||
for (auto&& oper : m_all_available_opers) {
|
||
auto comb = efficient_regex_calc(oper.skills);
|
||
comb.name_hash = oper.name_hash;
|
||
all_avaliable_combs.emplace_back(std::move(comb));
|
||
}
|
||
|
||
// 先把单个的技能按效率排个序,取效率最高的几个
|
||
std::vector<infrast::SkillsComb> optimal_combs;
|
||
optimal_combs.reserve(cur_max_num_of_opers);
|
||
double max_efficient = 0;
|
||
std::sort(all_avaliable_combs.begin(), all_avaliable_combs.end(),
|
||
[&](const infrast::SkillsComb& lhs, const infrast::SkillsComb& rhs) -> bool {
|
||
return lhs.efficient.at(m_product) > rhs.efficient.at(m_product);
|
||
});
|
||
|
||
for (const auto& comb : all_avaliable_combs) {
|
||
std::string skill_str;
|
||
for (const auto& skill : comb.skills) {
|
||
skill_str += skill.id + " ";
|
||
}
|
||
Log.trace(skill_str, comb.efficient.at(m_product));
|
||
}
|
||
|
||
std::unordered_map<std::string, int> skills_num;
|
||
for (int i = 0; i != m_all_available_opers.size(); ++i) {
|
||
auto comb = all_avaliable_combs.at(i);
|
||
|
||
bool out_of_num = false;
|
||
for (auto&& skill : comb.skills) {
|
||
if (skills_num[skill.id] >= skill.max_num) {
|
||
out_of_num = true;
|
||
break;
|
||
}
|
||
}
|
||
if (out_of_num) {
|
||
continue;
|
||
}
|
||
|
||
optimal_combs.emplace_back(comb);
|
||
max_efficient += all_avaliable_combs.at(i).efficient.at(m_product);
|
||
|
||
for (auto&& skill : comb.skills) {
|
||
++skills_num[skill.id];
|
||
}
|
||
|
||
if (optimal_combs.size() >= cur_max_num_of_opers) {
|
||
break;
|
||
}
|
||
}
|
||
|
||
{
|
||
std::string log_str = "[ ";
|
||
for (const auto& comb : optimal_combs) {
|
||
log_str += comb.desc.empty() ? comb.skills.begin()->names.front() : comb.desc;
|
||
log_str += "; ";
|
||
}
|
||
log_str += "]";
|
||
Log.trace("Single comb efficient", max_efficient, " , skills:", log_str);
|
||
}
|
||
|
||
// 如果有被锁住的干员,说明当前基建没升满级,组合就不启用
|
||
if (m_cur_num_of_lokced_opers != 0) {
|
||
m_optimal_combs = std::move(optimal_combs);
|
||
return true;
|
||
}
|
||
|
||
const int name_hash_thres = std::dynamic_pointer_cast<HashTaskInfo>(
|
||
Task.get("InfrastOperNameHash"))->dist_threshold;
|
||
|
||
// 遍历所有组合,找到效率最高的
|
||
auto& all_group = Resrc.infrast().get_skills_group(facility_name());
|
||
for (const infrast::SkillsGroup& group : all_group) {
|
||
Log.trace(group.desc);
|
||
auto cur_available_opers = all_avaliable_combs;
|
||
bool group_unavailable = false;
|
||
std::vector<infrast::SkillsComb> cur_combs;
|
||
cur_combs.reserve(cur_max_num_of_opers);
|
||
double cur_efficient = 0;
|
||
// 条件判断,不符合的直接过滤掉
|
||
bool meet_condition = true;
|
||
for (const auto& [cond, cond_value] : group.conditions) {
|
||
if (!Status.contains(cond)) {
|
||
continue;
|
||
}
|
||
// TODO:这里做成除了不等于,还可计算大于、小于等不同条件的
|
||
int cur_value = static_cast<int>(Status.get(cond));
|
||
if (cur_value != cond_value) {
|
||
meet_condition = false;
|
||
break;
|
||
}
|
||
}
|
||
if (!meet_condition) {
|
||
continue;
|
||
}
|
||
// necessary里的技能,一个都不能少
|
||
// TODO necessary暂时没做hash校验。因为没有需要比hash的necessary干员(
|
||
for (const infrast::SkillsComb& nec_skills : group.necessary) {
|
||
auto find_iter = std::find_if(
|
||
cur_available_opers.cbegin(), cur_available_opers.cend(),
|
||
[&](const infrast::SkillsComb& arg) -> bool {
|
||
return arg == nec_skills;
|
||
});
|
||
if (find_iter == cur_available_opers.cend()) {
|
||
group_unavailable = true;
|
||
break;
|
||
}
|
||
cur_combs.emplace_back(nec_skills);
|
||
if (auto iter = nec_skills.efficient_regex.find(m_product);
|
||
iter != nec_skills.efficient_regex.cend()) {
|
||
cur_efficient += efficient_regex_calc(nec_skills.skills).efficient.at(m_product);
|
||
}
|
||
else {
|
||
cur_efficient += nec_skills.efficient.at(m_product);
|
||
}
|
||
cur_available_opers.erase(find_iter);
|
||
}
|
||
if (group_unavailable) {
|
||
continue;
|
||
}
|
||
// 排个序,因为产物不同,效率可能会发生变化,所以配置文件里默认的顺序不一定准确
|
||
auto optional = group.optional;
|
||
for (auto&& opt : optional) {
|
||
if (auto iter = opt.efficient_regex.find(m_product);
|
||
iter != opt.efficient_regex.cend()) {
|
||
opt = efficient_regex_calc(opt.skills);
|
||
}
|
||
}
|
||
|
||
std::sort(optional.begin(), optional.end(),
|
||
[&](const infrast::SkillsComb& lhs,
|
||
const infrast::SkillsComb& rhs) -> bool {
|
||
return lhs.efficient.at(m_product) > rhs.efficient.at(m_product);
|
||
});
|
||
|
||
// 可能有多个干员有同样的技能,所以这里需要循环找同一个技能,直到找不到为止
|
||
for (const infrast::SkillsComb& opt : optional) {
|
||
auto find_iter = cur_available_opers.cbegin();
|
||
while (cur_combs.size() != cur_max_num_of_opers) {
|
||
find_iter = std::find_if(
|
||
find_iter, cur_available_opers.cend(),
|
||
[&](const infrast::SkillsComb& arg) -> bool {
|
||
return arg == opt;
|
||
});
|
||
if (find_iter != cur_available_opers.cend()) {
|
||
// 要求技能匹配的同时,hash也要匹配
|
||
bool hash_matched = false;
|
||
if (!opt.possible_hashs.empty()) {
|
||
for (const auto& [key, hash] : opt.possible_hashs) {
|
||
int dist = HashImageAnalyzer::hamming(find_iter->name_hash, hash);
|
||
Log.debug("optimal_calc | name hash dist", dist, hash, find_iter->name_hash);
|
||
if (dist < name_hash_thres) {
|
||
hash_matched = true;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
else {
|
||
hash_matched = true;
|
||
}
|
||
if (!hash_matched) {
|
||
++find_iter;
|
||
continue;
|
||
}
|
||
|
||
cur_combs.emplace_back(opt);
|
||
cur_efficient += opt.efficient.at(m_product);
|
||
find_iter = cur_available_opers.erase(find_iter);
|
||
}
|
||
else {
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
// 说明可选的没凑满人
|
||
if (cur_combs.size() < cur_max_num_of_opers) {
|
||
// 允许外部的话,就把单个干员凑进来
|
||
if (group.allow_external) {
|
||
for (size_t i = cur_combs.size(); i != cur_max_num_of_opers; ++i) {
|
||
size_t index = i - cur_combs.size();
|
||
cur_combs.emplace_back(cur_available_opers.at(index));
|
||
cur_efficient += cur_available_opers.at(index).efficient.at(m_product);
|
||
}
|
||
}
|
||
else { // 否则这个组合人不够,就不可用了
|
||
continue;
|
||
}
|
||
}
|
||
{
|
||
std::string log_str = "[ ";
|
||
for (const auto& comb : cur_combs) {
|
||
log_str += comb.desc.empty() ? comb.skills.begin()->names.front() : comb.desc;
|
||
log_str += "; ";
|
||
}
|
||
log_str += "]";
|
||
Log.trace(group.desc, "efficient", cur_efficient, " , skills:", log_str);
|
||
}
|
||
|
||
if (cur_efficient > max_efficient) {
|
||
optimal_combs = std::move(cur_combs);
|
||
max_efficient = cur_efficient;
|
||
}
|
||
}
|
||
{
|
||
std::string log_str = "[ ";
|
||
for (const auto& comb : optimal_combs) {
|
||
log_str += comb.desc.empty() ? comb.skills.begin()->names.front() : comb.desc;
|
||
log_str += "; ";
|
||
}
|
||
log_str += "]";
|
||
Log.trace("optimal efficient", max_efficient, " , skills:", log_str);
|
||
}
|
||
|
||
m_optimal_combs = std::move(optimal_combs);
|
||
|
||
return true;
|
||
}
|
||
|
||
bool asst::InfrastProductionTask::opers_choose()
|
||
{
|
||
LogTraceFunction;
|
||
bool has_error = false;
|
||
|
||
int count = 0;
|
||
auto& facility_info = Resrc.infrast().get_facility_info(facility_name());
|
||
int cur_max_num_of_opers = facility_info.max_num_of_opers - m_cur_num_of_lokced_opers;
|
||
|
||
const int name_hash_thres = std::dynamic_pointer_cast<HashTaskInfo>(
|
||
Task.get("InfrastOperNameHash"))->dist_threshold;
|
||
const int face_hash_thres = std::dynamic_pointer_cast<HashTaskInfo>(
|
||
Task.get("InfrastOperFaceHash"))->dist_threshold;
|
||
while (true) {
|
||
if (need_exit()) {
|
||
return false;
|
||
}
|
||
const auto image = Ctrler.get_image();
|
||
|
||
InfrastOperImageAnalyzer oper_analyzer(image);
|
||
oper_analyzer.set_facility(facility_name());
|
||
|
||
if (!oper_analyzer.analyze()) {
|
||
return false;
|
||
}
|
||
oper_analyzer.sort_by_loc();
|
||
|
||
// 这个情况一般是滑动/识别出错了,把所有的干员都滑过去了
|
||
if (oper_analyzer.get_num_of_opers_with_skills() == 0) {
|
||
if (!has_error) {
|
||
has_error = true;
|
||
// 倒回去再来一遍
|
||
swipe_to_the_left_of_operlist();
|
||
continue;
|
||
}
|
||
else {
|
||
// 如果已经出过一次错了,那就可能不是opers_choose出错,而是之前的opers_detect出错了
|
||
return false;
|
||
}
|
||
}
|
||
auto cur_all_opers = oper_analyzer.get_result();
|
||
Log.trace("before mood filter, opers size:", cur_all_opers.size());
|
||
// 小于心情阈值的干员则不可用
|
||
auto remove_iter = std::remove_if(cur_all_opers.begin(), cur_all_opers.end(),
|
||
[&](const infrast::Oper& rhs) -> bool {
|
||
return rhs.mood_ratio < m_mood_threshold;
|
||
});
|
||
cur_all_opers.erase(remove_iter, cur_all_opers.end());
|
||
Log.trace("after mood filter, opers size:", cur_all_opers.size());
|
||
|
||
for (auto opt_iter = m_optimal_combs.begin(); opt_iter != m_optimal_combs.end();) {
|
||
Log.trace("to find", opt_iter->skills.begin()->names.front());
|
||
auto find_iter = std::find_if(
|
||
cur_all_opers.cbegin(), cur_all_opers.cend(),
|
||
[&](const infrast::Oper& lhs) -> bool {
|
||
if (lhs.skills != opt_iter->skills) {
|
||
return false;
|
||
}
|
||
if (!opt_iter->hash_filter) {
|
||
return true;
|
||
}
|
||
else {
|
||
Log.trace("to comp hash");
|
||
// 既要技能相同,也要hash相同,双重校验
|
||
for (const auto& [_, hash] : opt_iter->possible_hashs) {
|
||
int dist = HashImageAnalyzer::hamming(lhs.name_hash, hash);
|
||
Log.debug("opers_choose | name hash dist", dist);
|
||
if (dist < name_hash_thres) {
|
||
return true;
|
||
}
|
||
}
|
||
return false;
|
||
}
|
||
});
|
||
|
||
if (find_iter == cur_all_opers.cend()) {
|
||
++opt_iter;
|
||
Log.trace("not found in this page");
|
||
continue;
|
||
}
|
||
Log.trace("found in this page");
|
||
// 这种情况可能是需要选择两个同样的技能,上一次循环选了一个,但是没有把滑出当前页面,本次又识别到了这个已选择的人
|
||
if (find_iter->selected == true) {
|
||
if (cur_max_num_of_opers != 1) {
|
||
cur_all_opers.erase(find_iter);
|
||
Log.trace("skill matched, but it's selected, pass");
|
||
continue;
|
||
}
|
||
// 但是如果当前设施只有一个位置,即不存在“上次循环”的情况,说明是清除干员按钮没点到
|
||
}
|
||
else {
|
||
Ctrler.click(find_iter->rect);
|
||
}
|
||
{
|
||
auto avlb_iter = std::find_if(
|
||
m_all_available_opers.cbegin(), m_all_available_opers.cend(),
|
||
[&](const infrast::Oper& lhs) -> bool {
|
||
int dist = HashImageAnalyzer::hamming(lhs.face_hash, find_iter->face_hash);
|
||
Log.debug("opers_choose | face hash dist", dist);
|
||
if (dist < face_hash_thres) {
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
);
|
||
if (avlb_iter != m_all_available_opers.cend()) {
|
||
m_all_available_opers.erase(avlb_iter);
|
||
}
|
||
else {
|
||
Log.error("opers_choose | not found oper");
|
||
}
|
||
}
|
||
++count;
|
||
cur_all_opers.erase(find_iter);
|
||
opt_iter = m_optimal_combs.erase(opt_iter);
|
||
}
|
||
if (m_optimal_combs.empty()) {
|
||
if (count >= cur_max_num_of_opers) {
|
||
break;
|
||
}
|
||
else { // 这种情况可能是萌新,可用干员人数不足以填满当前设施
|
||
callback(AsstMsg::SubTaskExtraInfo, basic_info_with_what("NotEnoughStaff"));
|
||
break;
|
||
}
|
||
}
|
||
|
||
// 因为识别完了还要点击,所以这里不能异步滑动
|
||
swipe_of_operlist();
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
bool asst::InfrastProductionTask::use_drone()
|
||
{
|
||
std::string task_name = "DroneAssist" + facility_name();
|
||
ProcessTask task_temp(*this, { task_name });
|
||
return task_temp.run();
|
||
}
|
||
|
||
asst::infrast::SkillsComb
|
||
asst::InfrastProductionTask::efficient_regex_calc(
|
||
std::unordered_set<infrast::Skill> skills) const
|
||
{
|
||
infrast::SkillsComb comb(std::move(skills));
|
||
// 根据正则,计算当前干员的实际效率
|
||
for (auto&& [product, formula] : comb.efficient_regex) {
|
||
std::string cur_formula = formula;
|
||
for (size_t pos = 0; pos != std::string::npos;) {
|
||
pos = cur_formula.find('[', pos);
|
||
if (pos == std::string::npos) {
|
||
break;
|
||
}
|
||
size_t rp_pos = cur_formula.find(']', pos);
|
||
if (rp_pos == std::string::npos) {
|
||
break;
|
||
// TODO 报错!
|
||
}
|
||
std::string status_key = cur_formula.substr(pos + 1, rp_pos - pos - 1);
|
||
int64_t status_value = Status.get(status_key);
|
||
cur_formula.replace(pos, rp_pos - pos + 1, std::to_string(status_value));
|
||
}
|
||
|
||
int eff = calculator::eval(cur_formula);
|
||
comb.efficient[product] = eff;
|
||
}
|
||
return comb;
|
||
}
|
||
|
||
bool asst::InfrastProductionTask::facility_list_detect()
|
||
{
|
||
LogTraceFunction;
|
||
m_facility_list_tabs.clear();
|
||
|
||
const auto image = Ctrler.get_image();
|
||
MultiMatchImageAnalyzer mm_analyzer(image);
|
||
mm_analyzer.set_task_info("InfrastFacilityListTab" + facility_name());
|
||
|
||
if (!mm_analyzer.analyze()) {
|
||
return false;
|
||
}
|
||
mm_analyzer.sort_result();
|
||
m_facility_list_tabs.reserve(mm_analyzer.get_result().size());
|
||
for (const auto& res : mm_analyzer.get_result()) {
|
||
m_facility_list_tabs.emplace_back(res.rect);
|
||
}
|
||
|
||
return true;
|
||
}
|