mirror of
https://github.com/MaaAssistantArknights/MaaAssistantArknights.git
synced 2026-07-19 10:32:19 +08:00
refactor: 重构 ProcessTask (#10001)
This commit is contained in:
@@ -15,337 +15,402 @@
|
||||
|
||||
using namespace asst;
|
||||
|
||||
asst::ProcessTask::ProcessTask(const AbstractTask& abs, std::vector<std::string> tasks_name)
|
||||
: AbstractTask(abs), m_raw_task_name_list(std::move(tasks_name))
|
||||
ProcessTask::ProcessTask(const AbstractTask& abs, std::vector<std::string> tasks_name) :
|
||||
AbstractTask(abs),
|
||||
m_begin_task_list(std::move(tasks_name))
|
||||
{
|
||||
m_task_delay = Config.get_options().task_delay;
|
||||
m_basic_info_cache = json::value();
|
||||
}
|
||||
|
||||
asst::ProcessTask::ProcessTask(AbstractTask&& abs, std::vector<std::string> tasks_name) noexcept
|
||||
: AbstractTask(std::move(abs)), m_raw_task_name_list(std::move(tasks_name))
|
||||
ProcessTask::ProcessTask(AbstractTask&& abs, std::vector<std::string> tasks_name) noexcept :
|
||||
AbstractTask(std::move(abs)),
|
||||
m_begin_task_list(std::move(tasks_name))
|
||||
{
|
||||
m_task_delay = Config.get_options().task_delay;
|
||||
m_basic_info_cache = json::value();
|
||||
}
|
||||
|
||||
bool asst::ProcessTask::run()
|
||||
{
|
||||
if (!m_enable) {
|
||||
Log.info("task disabled, pass", basic_info().to_string());
|
||||
return true;
|
||||
}
|
||||
if (m_task_delay == TaskDelayUnsetted) {
|
||||
m_task_delay = Config.get_options().task_delay;
|
||||
}
|
||||
|
||||
m_cur_task_name_list = m_raw_task_name_list;
|
||||
for (m_cur_retry = 0; m_cur_retry <= m_retry_times; ++m_cur_retry) {
|
||||
if (_run()) {
|
||||
return true;
|
||||
}
|
||||
if (need_exit()) {
|
||||
return false;
|
||||
}
|
||||
sleep(m_task_delay);
|
||||
}
|
||||
callback(AsstMsg::SubTaskError, basic_info());
|
||||
return on_run_fails();
|
||||
}
|
||||
|
||||
ProcessTask& asst::ProcessTask::set_task_delay(int delay) noexcept
|
||||
ProcessTask& ProcessTask::set_task_delay(int delay) noexcept
|
||||
{
|
||||
m_task_delay = delay;
|
||||
return *this;
|
||||
}
|
||||
|
||||
asst::ProcessTask& asst::ProcessTask::set_tasks(std::vector<std::string> tasks_name) noexcept
|
||||
ProcessTask& ProcessTask::set_tasks(std::vector<std::string> tasks_name) noexcept
|
||||
{
|
||||
m_cur_retry = 0;
|
||||
m_raw_task_name_list = std::move(tasks_name);
|
||||
m_begin_task_list = std::move(tasks_name);
|
||||
m_pre_task_name.clear();
|
||||
m_last_task_name.clear();
|
||||
return *this;
|
||||
}
|
||||
|
||||
ProcessTask& asst::ProcessTask::set_times_limit(std::string name, int limit, TimesLimitType type)
|
||||
ProcessTask& ProcessTask::set_times_limit(std::string name, int limit, TimesLimitType type)
|
||||
{
|
||||
m_times_limit[std::move(name)] = TimesLimitData { limit, type };
|
||||
return *this;
|
||||
}
|
||||
|
||||
ProcessTask& asst::ProcessTask::set_post_delay(std::string name, int delay)
|
||||
ProcessTask& ProcessTask::set_post_delay(std::string name, int delay)
|
||||
{
|
||||
m_post_delay[std::move(name)] = delay;
|
||||
return *this;
|
||||
}
|
||||
|
||||
ProcessTask& asst::ProcessTask::set_reusable_image(const cv::Mat& reusable)
|
||||
ProcessTask& ProcessTask::set_reusable_image(const cv::Mat& reusable)
|
||||
{
|
||||
m_reusable = reusable;
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool ProcessTask::_run()
|
||||
bool ProcessTask::run()
|
||||
{
|
||||
LogTraceFunction;
|
||||
|
||||
while (!m_cur_task_name_list.empty()) {
|
||||
if (need_exit()) {
|
||||
return false;
|
||||
}
|
||||
if (!m_enable) {
|
||||
Log.info("task disabled, pass", basic_info().to_string());
|
||||
return true;
|
||||
}
|
||||
if (m_cur_task_ptr && m_pre_task_name != m_cur_task_ptr->name) {
|
||||
m_pre_task_name = m_cur_task_ptr->name;
|
||||
}
|
||||
if (!m_enable) {
|
||||
Log.info("task disabled, pass", basic_info().to_string());
|
||||
return true;
|
||||
}
|
||||
|
||||
json::value info = basic_info();
|
||||
info["details"] = json::object {
|
||||
{ "to_be_recognized", json::array(m_cur_task_name_list) },
|
||||
{ "cur_retry", m_cur_retry },
|
||||
{ "retry_times", m_retry_times },
|
||||
};
|
||||
Log.info(info.to_string());
|
||||
if (m_begin_task_list.empty()) {
|
||||
Log.warn("task list is empty, pass", basic_info().to_string());
|
||||
return true;
|
||||
}
|
||||
|
||||
auto front_task_ptr = Task.get(m_cur_task_name_list.front());
|
||||
// 可能有配置错误,导致不存在对应的任务
|
||||
if (front_task_ptr == nullptr) {
|
||||
Log.error("Invalid task", m_cur_task_name_list.front());
|
||||
return false;
|
||||
}
|
||||
if (m_task_delay == TaskDelayUnsetted) {
|
||||
m_task_delay = Config.get_options().task_delay;
|
||||
}
|
||||
|
||||
Rect rect;
|
||||
json::value result;
|
||||
|
||||
// 如果第一个任务是JustReturn的,那就没必要再截图并计算了
|
||||
if (front_task_ptr->algorithm == AlgorithmType::JustReturn) {
|
||||
m_cur_task_ptr = front_task_ptr;
|
||||
}
|
||||
else {
|
||||
cv::Mat image = m_reusable.empty() ? ctrler()->get_image() : m_reusable;
|
||||
m_reusable = cv::Mat();
|
||||
PipelineAnalyzer analyzer(image, Rect(), m_inst);
|
||||
analyzer.set_tasks(m_cur_task_name_list);
|
||||
|
||||
auto res_opt = analyzer.analyze();
|
||||
if (!res_opt) {
|
||||
TaskConstPtr cur_task_ptr = nullptr; // 当前任务,仅用于计算 on_error_next
|
||||
TaskList to_be_recognized = m_begin_task_list; // 待匹配任务列表
|
||||
while (true) {
|
||||
auto [status /*识别成功与否*/, next_task_ptr /*匹配到的任务*/] = find_and_run_task(to_be_recognized);
|
||||
switch (status) {
|
||||
case NodeStatus::RetryFailed:
|
||||
// retry 次数达到上限,下一个匹配列表是 on_error_next,若没有则回调 SubTaskError
|
||||
if (cur_task_ptr == nullptr || cur_task_ptr->on_error_next.empty()) {
|
||||
callback(AsstMsg::SubTaskError, basic_info());
|
||||
return false;
|
||||
}
|
||||
m_cur_task_ptr = res_opt->task_ptr;
|
||||
if (m_cur_task_ptr->algorithm == AlgorithmType::MatchTemplate) {
|
||||
auto& raw_result = std::get<0>(res_opt->result);
|
||||
result = json::object { { "score", raw_result.score } };
|
||||
}
|
||||
else if (m_cur_task_ptr->algorithm == AlgorithmType::OcrDetect) {
|
||||
auto& raw_result = std::get<1>(res_opt->result);
|
||||
result = json::object { { "score", raw_result.score }, { "text", raw_result.text } };
|
||||
}
|
||||
rect = res_opt->rect;
|
||||
}
|
||||
if (need_exit()) {
|
||||
return false;
|
||||
}
|
||||
m_last_task_name = m_cur_task_ptr->name;
|
||||
|
||||
int& exec_times = m_exec_times[m_last_task_name];
|
||||
|
||||
auto [max_times, limit_type] = calc_time_limit();
|
||||
|
||||
if (limit_type == TimesLimitType::Pre && exec_times >= max_times) {
|
||||
info["what"] = "ExceededLimit";
|
||||
info["details"] = json::object {
|
||||
{ "task", m_last_task_name },
|
||||
{ "exec_times", exec_times },
|
||||
{ "max_times", max_times },
|
||||
{ "limit_type", "pre" },
|
||||
};
|
||||
Log.info("exec times exceeded the limit", info.to_string());
|
||||
callback(AsstMsg::SubTaskExtraInfo, info);
|
||||
m_cur_task_name_list = m_cur_task_ptr->exceeded_next;
|
||||
sleep(m_task_delay);
|
||||
continue;
|
||||
}
|
||||
|
||||
m_cur_retry = 0;
|
||||
++exec_times;
|
||||
|
||||
info["details"] = json::object {
|
||||
{ "task", m_last_task_name },
|
||||
{ "action", enum_to_string(m_cur_task_ptr->action) },
|
||||
{ "exec_times", exec_times },
|
||||
{ "max_times", max_times },
|
||||
{ "algorithm", enum_to_string(m_cur_task_ptr->algorithm) },
|
||||
{ "result", result },
|
||||
};
|
||||
|
||||
callback(AsstMsg::SubTaskStart, info);
|
||||
// 允许插件停用ProcessTask
|
||||
if (!m_enable) {
|
||||
Log.info("task disabled after SubTaskStart callback, pass", basic_info().to_string());
|
||||
to_be_recognized = cur_task_ptr->on_error_next;
|
||||
break;
|
||||
case NodeStatus::Runout:
|
||||
// 成功匹配但执行次数达到上限,下一个匹配列表是 exceeded_next
|
||||
to_be_recognized = next_task_ptr->exceeded_next;
|
||||
break;
|
||||
case NodeStatus::Success:
|
||||
// 成功匹配且执行成功,下一个匹配列表是 next
|
||||
to_be_recognized = next_task_ptr->next;
|
||||
break;
|
||||
case NodeStatus::Interrupted:
|
||||
// need_exit() or Stop action
|
||||
return true;
|
||||
}
|
||||
|
||||
// 前置固定延时
|
||||
if (!sleep(m_cur_task_ptr->pre_delay)) {
|
||||
case NodeStatus::InternalError:
|
||||
callback(AsstMsg::SubTaskError, basic_info());
|
||||
return false;
|
||||
}
|
||||
|
||||
bool need_stop = false;
|
||||
switch (m_cur_task_ptr->action) {
|
||||
case ProcessTaskAction::ClickRect:
|
||||
rect = m_cur_task_ptr->specific_rect;
|
||||
exec_click_task(rect);
|
||||
break;
|
||||
case ProcessTaskAction::ClickSelf:
|
||||
if (const auto& res_move = m_cur_task_ptr->rect_move; !res_move.empty()) {
|
||||
rect = rect.move(res_move);
|
||||
}
|
||||
exec_click_task(rect);
|
||||
break;
|
||||
case ProcessTaskAction::Swipe:
|
||||
exec_swipe_task(m_cur_task_ptr->specific_rect, m_cur_task_ptr->rect_move,
|
||||
m_cur_task_ptr->special_params.empty() ? 0 : m_cur_task_ptr->special_params.at(0),
|
||||
(m_cur_task_ptr->special_params.size() < 2) ? false : m_cur_task_ptr->special_params.at(1),
|
||||
(m_cur_task_ptr->special_params.size() < 3) ? 1 : m_cur_task_ptr->special_params.at(2),
|
||||
(m_cur_task_ptr->special_params.size() < 4) ? 1 : m_cur_task_ptr->special_params.at(3));
|
||||
break;
|
||||
case ProcessTaskAction::DoNothing:
|
||||
break;
|
||||
case ProcessTaskAction::Stop:
|
||||
Log.info("stop action", info.to_string());
|
||||
need_stop = true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
status()->set_number(Status::ProcessTaskLastTimePrefix + m_last_task_name, time(nullptr));
|
||||
|
||||
// 减少其他任务的执行次数
|
||||
// 例如,进入吃理智药的界面了,相当于上一次点蓝色开始行动没生效
|
||||
// 所以要给蓝色开始行动的次数减一
|
||||
for (const std::string& reduce : m_cur_task_ptr->reduce_other_times) {
|
||||
auto& v = m_exec_times[reduce];
|
||||
if (v > 0) {
|
||||
--v;
|
||||
Log.trace("Task `", m_cur_task_ptr->name, "` reduce `", reduce, "` times to ", v);
|
||||
}
|
||||
else {
|
||||
Log.trace("Task `", m_cur_task_ptr->name, "` attempt to reduce `", reduce,
|
||||
"` times, but it is already 0");
|
||||
}
|
||||
}
|
||||
|
||||
// 后置固定延时
|
||||
if (!sleep(calc_post_delay())) {
|
||||
Log.error(__FUNCTION__, "| Unknown status", static_cast<int>(status));
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const std::string& sub : m_cur_task_ptr->sub) {
|
||||
LogTraceScope("Sub: " + sub);
|
||||
bool sub_ret = ProcessTask(*this, { sub }).run();
|
||||
if (!sub_ret && !m_cur_task_ptr->sub_error_ignored) {
|
||||
Log.error("Sub error and not ignored", sub);
|
||||
// 子任务如果失败了,一定已经经历过子任务自己的 m_retry_times 次重试了
|
||||
// 这时候即使再重试父任务也没有意义,直接把父任务也跟着报错出去
|
||||
m_cur_retry = m_retry_times;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
callback(AsstMsg::SubTaskCompleted, info);
|
||||
// 允许插件停用ProcessTask
|
||||
if (!m_enable) {
|
||||
Log.info("task disabled after SubTaskCompleted callback, pass", basic_info().to_string());
|
||||
if (to_be_recognized.empty()) { // Finished, skip delay
|
||||
return true;
|
||||
}
|
||||
|
||||
if (limit_type == TimesLimitType::Post && exec_times >= max_times) {
|
||||
info["what"] = "ExceededLimit";
|
||||
info["details"] = json::object {
|
||||
{ "task", m_last_task_name },
|
||||
{ "exec_times", exec_times },
|
||||
{ "max_times", max_times },
|
||||
{ "limit_type", "post" },
|
||||
};
|
||||
Log.info("exec times exceeded the limit", info.to_string());
|
||||
callback(AsstMsg::SubTaskExtraInfo, info);
|
||||
m_cur_task_name_list = m_cur_task_ptr->exceeded_next;
|
||||
sleep(m_task_delay);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (m_cur_task_ptr->next.empty()) {
|
||||
need_stop = true;
|
||||
}
|
||||
|
||||
if (need_stop) {
|
||||
if (!sleep(m_task_delay)) { // Interrupted
|
||||
return true;
|
||||
}
|
||||
m_cur_task_name_list = m_cur_task_ptr->next;
|
||||
sleep(m_task_delay);
|
||||
|
||||
cur_task_ptr = next_task_ptr;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool asst::ProcessTask::on_run_fails()
|
||||
bool ProcessTask::_run()
|
||||
{
|
||||
LogTraceFunction;
|
||||
|
||||
if (!m_cur_task_ptr || m_cur_task_ptr->on_error_next.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
set_tasks(m_cur_task_ptr->on_error_next);
|
||||
return run();
|
||||
Log.error(__FUNCTION__, "should not be called");
|
||||
#ifdef ASST_DEBUG
|
||||
throw std::runtime_error("ProcessTask::_run() should not be called");
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
std::pair<int, asst::ProcessTask::TimesLimitType> asst::ProcessTask::calc_time_limit() const
|
||||
ProcessTask::HitDetail ProcessTask::find_first(const TaskList& list) /* const, except m_reusable */
|
||||
{
|
||||
// eg. "C@B@A" 的 max_times 取 "C@B@A", "B@A", "A" 中有 max_times 定义的最靠前者
|
||||
for (std::string cur_base_name = m_cur_task_ptr->name;;) {
|
||||
if (auto iter = m_times_limit.find(cur_base_name); iter != m_times_limit.cend()) {
|
||||
return { iter->second.times, iter->second.type };
|
||||
if (list.empty()) [[unlikely]] {
|
||||
Log.warn(__FUNCTION__, "| empty task list");
|
||||
return { .task_ptr = nullptr };
|
||||
}
|
||||
|
||||
// 如果第一个任务是JustReturn的,那就没必要再截图并计算了
|
||||
TaskConstPtr task_ptr = Task.get(list.front());
|
||||
if (task_ptr != nullptr && task_ptr->algorithm == AlgorithmType::JustReturn) {
|
||||
return { .task_ptr = std::move(task_ptr) };
|
||||
}
|
||||
|
||||
cv::Mat image = m_reusable.empty() ? ctrler()->get_image() : m_reusable;
|
||||
m_reusable = cv::Mat();
|
||||
PipelineAnalyzer analyzer(image, Rect(), m_inst);
|
||||
analyzer.set_tasks(list);
|
||||
|
||||
auto res_opt = analyzer.analyze();
|
||||
if (!res_opt) {
|
||||
return { .task_ptr = nullptr };
|
||||
}
|
||||
|
||||
task_ptr = std::move(res_opt->task_ptr);
|
||||
|
||||
if (task_ptr->algorithm == AlgorithmType::MatchTemplate) {
|
||||
auto& raw_result = std::get<0>(res_opt->result);
|
||||
return { .rect = res_opt->rect,
|
||||
.reco_detail = json::object { { "score", raw_result.score } },
|
||||
.task_ptr = task_ptr };
|
||||
}
|
||||
|
||||
if (task_ptr->algorithm == AlgorithmType::OcrDetect) {
|
||||
auto& raw_result = std::get<1>(res_opt->result);
|
||||
return { .rect = res_opt->rect,
|
||||
.reco_detail = json::object { { "score", raw_result.score }, { "text", raw_result.text } },
|
||||
.task_ptr = task_ptr };
|
||||
}
|
||||
|
||||
return { .rect = res_opt->rect, .task_ptr = task_ptr };
|
||||
}
|
||||
|
||||
// action 为 Stop 时返回 Interrupted, 其它返回 Success
|
||||
ProcessTask::NodeStatus ProcessTask::run_action(const HitDetail& hits) const
|
||||
{
|
||||
const auto& task = hits.task_ptr;
|
||||
switch (task->action) {
|
||||
case ProcessTaskAction::ClickRect:
|
||||
exec_click_task(task->specific_rect);
|
||||
return NodeStatus::Success;
|
||||
case ProcessTaskAction::ClickSelf: {
|
||||
Rect rect = hits.rect;
|
||||
if (const auto& res_move = task->rect_move; !res_move.empty()) {
|
||||
rect = rect.move(res_move);
|
||||
}
|
||||
size_t at_pos = cur_base_name.find('@');
|
||||
exec_click_task(rect);
|
||||
return NodeStatus::Success;
|
||||
}
|
||||
case ProcessTaskAction::Swipe: {
|
||||
size_t param_size = task->special_params.size();
|
||||
// Warning: 这里的后两个参数 slope_in 和 slope_out 是 double 类型,但是在 json 中是 int 类型
|
||||
exec_swipe_task(
|
||||
task->specific_rect,
|
||||
task->rect_move,
|
||||
(param_size > 0) ? task->special_params.at(0) : 0,
|
||||
(param_size > 1) ? task->special_params.at(1) : false,
|
||||
(param_size > 2) ? task->special_params.at(2) : 1,
|
||||
(param_size > 3) ? task->special_params.at(3) : 1);
|
||||
return NodeStatus::Success;
|
||||
}
|
||||
case ProcessTaskAction::DoNothing:
|
||||
return NodeStatus::Success;
|
||||
case ProcessTaskAction::Stop:
|
||||
Log.info("Action: Stop");
|
||||
return NodeStatus::Interrupted;
|
||||
default:
|
||||
return NodeStatus::Success;
|
||||
}
|
||||
};
|
||||
|
||||
ProcessTask::NodeStatus ProcessTask::run_task(const HitDetail& hits)
|
||||
{
|
||||
const auto& task = hits.task_ptr;
|
||||
const auto& task_name = task->name;
|
||||
int& exec_times = m_exec_times[task_name];
|
||||
auto [max_times, limit_type] = calc_time_limit(task);
|
||||
json::value info = basic_info();
|
||||
|
||||
if (exec_times >= max_times && limit_type == TimesLimitType::Pre) {
|
||||
info["what"] = "ExceededLimit";
|
||||
info["details"] = json::object {
|
||||
{ "task", task_name },
|
||||
{ "exec_times", exec_times },
|
||||
{ "max_times", max_times },
|
||||
};
|
||||
Log.info("exec times exceeded the limit", info.to_string());
|
||||
callback(AsstMsg::SubTaskExtraInfo, info);
|
||||
return NodeStatus::Runout;
|
||||
}
|
||||
|
||||
++exec_times;
|
||||
|
||||
info["details"] = json::object {
|
||||
{ "task", task_name },
|
||||
{ "exec_times", exec_times },
|
||||
{ "max_times", max_times },
|
||||
{ "action", enum_to_string(task->action) },
|
||||
{ "algorithm", enum_to_string(task->algorithm) },
|
||||
{ "result", hits.reco_detail },
|
||||
};
|
||||
|
||||
callback(AsstMsg::SubTaskStart, info);
|
||||
// 允许插件停用ProcessTask
|
||||
if (!m_enable) {
|
||||
Log.info("task disabled after SubTaskStart callback, pass", basic_info().to_string());
|
||||
return NodeStatus::Interrupted;
|
||||
}
|
||||
|
||||
// 前置固定延时
|
||||
if (!sleep(task->pre_delay)) {
|
||||
return NodeStatus::Interrupted;
|
||||
}
|
||||
|
||||
// 根据任务的 action 执行任务
|
||||
if (auto result = run_action(hits); result != NodeStatus::Success) {
|
||||
return result;
|
||||
}
|
||||
|
||||
status()->set_number(Status::ProcessTaskLastTimePrefix + task_name, time(nullptr));
|
||||
|
||||
// 减少其他任务的执行次数
|
||||
// 例如,进入吃理智药的界面了,相当于上一次点蓝色开始行动没生效
|
||||
// 所以要给蓝色开始行动的次数减一
|
||||
for (const std::string& other_task : task->reduce_other_times) {
|
||||
if (int& v = m_exec_times[other_task]; v > 0) {
|
||||
--v;
|
||||
Log.trace("task `", task_name, "` reduce `", other_task, "` exec times to", v);
|
||||
}
|
||||
}
|
||||
|
||||
// 后置固定延时
|
||||
if (!sleep(calc_post_delay(task))) {
|
||||
return NodeStatus::Interrupted;
|
||||
}
|
||||
|
||||
for (const std::string& sub : task->sub) {
|
||||
LogTraceScope("Sub: " + sub);
|
||||
bool sub_ret = ProcessTask(*this, { sub }).run();
|
||||
if (!sub_ret && !task->sub_error_ignored) {
|
||||
Log.error("Sub error and not ignored", sub);
|
||||
// 感觉应该把 run 改成 NodeStatus 类型,这样可以知道 sub 的具体执行结果
|
||||
return NodeStatus::InternalError;
|
||||
}
|
||||
if (need_exit()) {
|
||||
return NodeStatus::Interrupted;
|
||||
}
|
||||
}
|
||||
|
||||
callback(AsstMsg::SubTaskCompleted, info);
|
||||
// 允许插件停用ProcessTask
|
||||
if (!m_enable) {
|
||||
Log.info("task disabled after SubTaskCompleted callback, pass", basic_info().to_string());
|
||||
return NodeStatus::Interrupted;
|
||||
}
|
||||
|
||||
if (exec_times >= max_times && limit_type == TimesLimitType::Post) {
|
||||
info["what"] = "ExceededLimit";
|
||||
info["details"] = json::object {
|
||||
{ "task", task_name },
|
||||
{ "exec_times", exec_times },
|
||||
{ "max_times", max_times },
|
||||
};
|
||||
Log.info("exec times exceeded the limit", info.to_string());
|
||||
callback(AsstMsg::SubTaskExtraInfo, info);
|
||||
return NodeStatus::Runout;
|
||||
}
|
||||
|
||||
return NodeStatus::Success;
|
||||
}
|
||||
|
||||
// 保证 first 为 Success 或 Runout 时 second 不为 nullptr
|
||||
std::pair<ProcessTask::NodeStatus, TaskConstPtr> ProcessTask::find_and_run_task(const TaskList& list)
|
||||
{
|
||||
if (need_exit()) {
|
||||
return { NodeStatus::Interrupted, nullptr };
|
||||
}
|
||||
|
||||
if (!m_enable) {
|
||||
Log.info("task disabled, pass", basic_info().to_string());
|
||||
return { NodeStatus::Interrupted, nullptr };
|
||||
}
|
||||
|
||||
m_pre_task_name = std::move(m_last_task_name);
|
||||
|
||||
HitDetail hits;
|
||||
for (int cur_retry = 0; cur_retry <= m_retry_times; ++cur_retry) {
|
||||
json::value info = basic_info();
|
||||
info["details"] = json::object {
|
||||
{ "to_be_recognized", json::array(list) },
|
||||
{ "cur_retry", cur_retry },
|
||||
{ "retry_times", m_retry_times },
|
||||
};
|
||||
Log.info(info.to_string());
|
||||
|
||||
if (cur_retry != 0 && !sleep(m_task_delay)) {
|
||||
return { NodeStatus::Interrupted, nullptr };
|
||||
}
|
||||
if (hits = find_first(list); hits.task_ptr != nullptr) {
|
||||
m_last_task_name = hits.task_ptr->name;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (hits.task_ptr == nullptr) {
|
||||
return { NodeStatus::RetryFailed, nullptr };
|
||||
}
|
||||
|
||||
if (need_exit()) {
|
||||
return { NodeStatus::Interrupted, nullptr };
|
||||
}
|
||||
|
||||
return { run_task(hits), hits.task_ptr };
|
||||
}
|
||||
|
||||
ProcessTask::TimesLimitData ProcessTask::calc_time_limit(TaskConstPtr task) const
|
||||
{
|
||||
std::string task_name = task->name;
|
||||
// eg. "C@B@A" 的 times_limit 取 "C@B@A", "B@A", "A" 中有设置 m_times_limit 的最靠前者,否则取 task 的值
|
||||
while (true) {
|
||||
if (auto iter = m_times_limit.find(task_name); iter != m_times_limit.cend()) {
|
||||
return { .times = iter->second.times, .type = iter->second.type };
|
||||
}
|
||||
size_t at_pos = task_name.find('@');
|
||||
if (at_pos == std::string::npos) {
|
||||
return { m_cur_task_ptr->max_times, TimesLimitType::Pre };
|
||||
return { .times = task->max_times, .type = TimesLimitType::Pre };
|
||||
}
|
||||
cur_base_name = cur_base_name.substr(at_pos + 1);
|
||||
task_name = task_name.substr(at_pos + 1);
|
||||
}
|
||||
}
|
||||
|
||||
int asst::ProcessTask::calc_post_delay() const
|
||||
int ProcessTask::calc_post_delay(TaskConstPtr task) const
|
||||
{
|
||||
// eg. "C@B@A" 的 max_times 取 "C@B@A", "B@A", "A" 中有 max_times 定义的最靠前者
|
||||
for (std::string cur_base_name = m_cur_task_ptr->name;;) {
|
||||
if (auto iter = m_post_delay.find(cur_base_name); iter != m_post_delay.cend()) {
|
||||
std::string task_name = task->name;
|
||||
// eg. "C@B@A" 的 post_delay 取 "C@B@A", "B@A", "A" 中有设置 m_post_delay 的最靠前者,否则取 task 的值
|
||||
while (true) {
|
||||
if (auto iter = m_post_delay.find(task_name); iter != m_post_delay.cend()) {
|
||||
return iter->second;
|
||||
}
|
||||
size_t at_pos = cur_base_name.find('@');
|
||||
size_t at_pos = task_name.find('@');
|
||||
if (at_pos == std::string::npos) {
|
||||
return m_cur_task_ptr->post_delay;
|
||||
return task->post_delay;
|
||||
}
|
||||
cur_base_name = cur_base_name.substr(at_pos + 1);
|
||||
task_name = task_name.substr(at_pos + 1);
|
||||
}
|
||||
}
|
||||
|
||||
json::value asst::ProcessTask::basic_info() const
|
||||
json::value ProcessTask::basic_info() const
|
||||
{
|
||||
return AbstractTask::basic_info() |
|
||||
json::object { { "first", json::array(m_raw_task_name_list) }, { "pre_task", m_pre_task_name } };
|
||||
json::object { { "first", json::array(m_begin_task_list) }, { "pre_task", m_pre_task_name } };
|
||||
}
|
||||
|
||||
void ProcessTask::exec_click_task(const Rect& matched_rect)
|
||||
void ProcessTask::exec_click_task(const Rect& matched_rect) const
|
||||
{
|
||||
ctrler()->click(matched_rect);
|
||||
}
|
||||
|
||||
void asst::ProcessTask::exec_swipe_task(const Rect& r1, const Rect& r2, int duration, bool extra_swipe, double slope_in,
|
||||
double slope_out)
|
||||
void ProcessTask::exec_swipe_task(
|
||||
const Rect& r1,
|
||||
const Rect& r2,
|
||||
int duration,
|
||||
bool extra_swipe,
|
||||
double slope_in,
|
||||
double slope_out) const
|
||||
{
|
||||
ctrler()->swipe(r1, r2, duration, extra_swipe, slope_in, slope_out);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user