为基建任务添加回调消息

This commit is contained in:
MistEO
2021-09-12 23:55:42 +08:00
parent 924f383dac
commit 3d9dad8e07
15 changed files with 209 additions and 76 deletions

View File

@@ -152,6 +152,7 @@
"ClickCorner_Doc": "点击右下角,既不会点到掉落物品,又能点到蓝色开始按钮(为了容错)的一块位置。",
"algorithm": "justreturn",
"action": "clickRect",
"rearDelay": 2000,
"specificArea": [
1100,
700,

View File

@@ -60,10 +60,10 @@ bool AbstractTask::sleep(unsigned millisecond)
bool AbstractTask::print_window(const std::string& dir)
{
const cv::Mat& image = m_controller_ptr->get_image(true);
if (image.empty()) {
return false;
}
//const cv::Mat& image = m_controller_ptr->get_image(true);
//if (image.empty()) {
// return false;
//}
// 现在用adb直接截图了不用裁剪了
//// 保存的截图额外再裁剪掉一圈,不然企鹅物流识别不出来
//int offset = Configer::get_instance().m_options.print_window_crop_offset;
@@ -73,15 +73,18 @@ bool AbstractTask::print_window(const std::string& dir)
const std::string time_str = StringReplaceAll(StringReplaceAll(GetFormatTimeString(), " ", "_"), ":", "-");
const std::string filename = dir + time_str + ".png";
bool ret = cv::imwrite(filename.c_str(), image);
//bool ret = cv::imwrite(filename.c_str(), image);
// 直接把Adb上次pull出来的拷贝过去就行了
std::filesystem::copy(m_controller_ptr->ScreenFilename, filename);
json::value callback_json;
callback_json["filename"] = filename;
callback_json["ret"] = ret;
callback_json["ret"] = true;
callback_json["offset"] = 0;
m_callback(AsstMsg::PrintWindow, callback_json, m_callback_arg);
return ret;
return true;
}
bool asst::AbstractTask::need_exit() const noexcept

View File

@@ -29,6 +29,7 @@ namespace asst {
virtual int get_retry_times() { return m_retry_times; }
virtual void set_task_chain(std::string name) { m_task_chain = std::move(name); }
virtual const std::string& get_task_chain() { return m_task_chain; }
virtual void on_run_fails(int retry_times) { ; }
protected:
virtual bool sleep(unsigned millisecond);
virtual bool print_window(const std::string& dir);

View File

@@ -459,6 +459,7 @@ void Assistance::working_proc()
++retry_times;
// 执行失败再还原回去
m_tasks_deque.emplace_front(task_ptr);
task_ptr->on_run_fails(retry_times);
}
}
@@ -515,9 +516,9 @@ void Assistance::task_callback(AsstMsg msg, const json::value& detail, void* cus
case AsstMsg::ImageIsEmpty:
p_this->stop(false);
break;
case AsstMsg::WindowMinimized:
p_this->m_controller_ptr->show_window();
break;
//case AsstMsg::WindowMinimized:
// p_this->m_controller_ptr->show_window();
// break;
case AsstMsg::AppendProcessTask:
more_detail["type"] = "ProcessTask";
[[fallthrough]];

View File

@@ -10,34 +10,41 @@
namespace asst {
enum class AsstMsg {
/* Error Msg */
PtrIsNull,
ImageIsEmpty,
WindowMinimized,
InitFaild,
/* Info Msg */
TaskStart,
ImageFindResult,
ImageMatched,
TaskMatched,
ReachedLimit,
ReadyToSleep,
EndOfSleep,
AppendProcessTask, // 这个消息Assistance会新增任务,外部不需要处理
AppendTask, // 这个消息Assistance会新增任务,外部不需要处理
TaskCompleted,
PrintWindow,
TaskStop,
TaskError,
PtrIsNull, // 指针为空
ImageIsEmpty, // 图像为空
WindowMinimized, // [已弃用] 窗口被最小化了
InitFaild, // 初始化失败
TaskError, // 任务错误任务一直出错retry次数达到上限
OcrResultError, // Ocr识别结果错误
/* Info Msg: about Task */
TaskStart = 1000, // 任务开始
TaskMatched, // 任务匹配成功
ReachedLimit, // 单个原子任务达到次数上限
ReadyToSleep, // 准备开始睡眠
EndOfSleep, // 睡眠结束
AppendProcessTask, // 新增流程任务,Assistance内部消息,外部不需要处理
AppendTask, // 新增任务,Assistance内部消息,外部不需要处理
TaskCompleted, // 单个原子任务完成
PrintWindow, // 截图消息
TaskStop, // 任务停止
/* Info Msg: about Identify */
TextDetected = 2000, // 识别到文字
ImageFindResult, // 查找图像的结果
ImageMatched, // 图像匹配成功
/* Open Recruit Msg */
TextDetected,
RecruitTagsDetected,
OcrResultError,
RecruitSpecialTag,
RecruitResult,
/* Infrast Msg*/
OpersDetected, // 识别到了干员s
OpersIdtfResult, // 干员识别结果(总的)
InfrastComb // 当前设置的最优干员组合
RecruitTagsDetected = 3000, // 公招识别到了Tags
RecruitSpecialTag, // 公招识别到了特殊的Tag
RecruitResult, // 公开招募结果
/* Infrast Msg */
OpersDetected = 4000, // 识别到了干员s
OpersIdtfResult, // 干员识别结果(总的)
InfrastComb, // 当前房间的最优干员组合
EnterStation, // 进入某个房间
StationInfo, // 当前房间信息
ReadyToShift, // 准备换班
ShiftCompleted, // 换班完成(单个房间)
NoNeedToShift // 无需换班(单个房间)
};
static std::ostream& operator<<(std::ostream& os, const AsstMsg& type)
@@ -67,7 +74,12 @@ namespace asst {
{AsstMsg::AppendTask, "AppendTask"},
{AsstMsg::OpersDetected, "OpersDetected"},
{AsstMsg::OpersIdtfResult, "OpersIdtfResult"},
{AsstMsg::InfrastComb, "InfrastComb"}
{AsstMsg::InfrastComb, "InfrastComb"},
{AsstMsg::EnterStation, "EnterStation"},
{AsstMsg::StationInfo, "StationInfo"},
{AsstMsg::ReadyToShift, "ReadyToShift"},
{AsstMsg::ShiftCompleted, "ShiftCompleted"},
{AsstMsg::NoNeedToShift, "NoNeedToShift"}
};
return os << _type_name.at(type);
}

View File

@@ -310,21 +310,46 @@ bool asst::InfrastAbstractTask::enter_station(const std::vector<std::string>& te
cv::Mat image = m_controller_ptr->get_image();
std::vector<asst::Identify::FindImageResult> max_score_reslut;
json::value callback_json;
callback_json["algorithm"] = "MatchTemplate";
callback_json["threshold"] = threshold;
std::string max_score_name;
for (const auto& templ : templ_names) {
auto cur_result = m_identify_ptr->find_all_images(image, templ, threshold);
callback_json["name"] = templ;
if (cur_result.empty()) {
callback_json["value"] = 0;
callback_json["rect"] = json::array({ 0, 0, 0, 0 });
m_callback(AsstMsg::ImageFindResult, callback_json, m_callback_arg);
continue;
}
Rect& rect = cur_result.at(0).rect;
callback_json["value"] = cur_result.at(0).score;
callback_json["rect"] = json::array({ rect.x, rect.y, rect.width, rect.height });
m_callback(AsstMsg::ImageFindResult, callback_json, m_callback_arg);
if (max_score_reslut.empty()
|| cur_result.at(0).score > max_score_reslut.at(0).score) { // find_all_image里是排过序的直接取第一个就是最大得分
max_score_reslut = std::move(cur_result);
max_score_name = templ;
}
}
if (max_score_reslut.empty()) {
DebugTraceError("The number of matches is empty");
return false;
}
callback_json["name"] = max_score_name;
Rect& rect = max_score_reslut.at(0).rect;
callback_json["size"] = max_score_reslut.size();
callback_json["value"] = max_score_reslut.at(0).score;
callback_json["rect"] = json::array({ rect.x, rect.y, rect.width, rect.height });
m_callback(AsstMsg::ImageMatched, callback_json, m_callback_arg);
if (index >= max_score_reslut.size()) {
DebugTraceError("The number of matches is too few", index, max_score_reslut.size());
return false;
}

View File

@@ -10,6 +10,9 @@ namespace asst {
InfrastAbstractTask(AsstCallback callback, void* callback_arg);
virtual ~InfrastAbstractTask() = default;
virtual bool run() = 0;
virtual void on_run_fails(int retry_times) override {
append_task_to_back_to_infrast_home();
}
protected:
virtual bool swipe_to_the_left(); // 滑动到最左侧
virtual bool click_clear_button();

View File

@@ -20,9 +20,14 @@ bool asst::InfrastDormTask::run()
return false;
}
json::value task_start_json = json::object{
{ "task_type", "InfrastDormTask" },
{ "task_chain", m_task_chain}
};
m_callback(AsstMsg::TaskStart, task_start_json, m_callback_arg);
bool ret = enter_station({ "Dorm", "DormMini" }, m_dorm_begin, 0.8);
if (!ret) {
append_task_to_back_to_infrast_home();
return false;
}
@@ -39,6 +44,14 @@ bool asst::InfrastDormTask::run()
else {
to_left = true; // 第一个进入的宿舍需要滑动到最左侧一下。后面的宿舍都不用了
}
if (!ret) {
return false;
}
json::value enter_json;
enter_json["station"] = "Dorm";
enter_json["index"] = dorm_index;
m_callback(AsstMsg::EnterStation, enter_json, m_callback_arg);
enter_operator_selection();
int selected = select_operators(to_left);
if (selected == -1) {
@@ -51,6 +64,8 @@ bool asst::InfrastDormTask::run()
}
m_dorm_begin = dorm_index;
m_callback(AsstMsg::TaskCompleted, task_start_json, m_callback_arg);
return ret;
}
@@ -168,10 +183,14 @@ int asst::InfrastDormTask::select_operators(bool need_to_the_left)
}
cv::Mat image = m_controller_ptr->get_image();
json::value shift_json;
shift_json["station"] = "Dorm";
// 识别“休息中”的干员
auto resting_result = m_identify_ptr->find_all_images(image, "Resting", 0.8);
if (resting_result.size() == MaxOperNumInDorm) { // 如果所有人都在休息,那这个宿舍不用换班,直接关了
click_return_button();
m_callback(AsstMsg::NoNeedToShift, shift_json, m_callback_arg);
return resting_result.size();
}
@@ -182,6 +201,8 @@ int asst::InfrastDormTask::select_operators(bool need_to_the_left)
double mood_threshold = Configer::get_instance().m_infrast_options.dorm_threshold;
auto work_mood_result = detect_mood_status_at_work(image, mood_threshold);
m_callback(AsstMsg::ReadyToShift, shift_json, m_callback_arg);
click_clear_button();
// 把“休息中”的干员,都再次选上
for (const auto& resting : resting_result) {
@@ -207,6 +228,7 @@ int asst::InfrastDormTask::select_operators(bool need_to_the_left)
}
if (count >= MaxOperNumInDorm) {
click_confirm_button();
m_callback(AsstMsg::ShiftCompleted, shift_json, m_callback_arg);
return count;
}
@@ -236,6 +258,7 @@ int asst::InfrastDormTask::select_operators(bool need_to_the_left)
}
click_confirm_button();
m_callback(AsstMsg::ShiftCompleted, shift_json, m_callback_arg);
return count;
}

View File

@@ -10,6 +10,7 @@ namespace asst {
virtual ~InfrastDormTask() = default;
virtual bool run() override;
bool set_dorm_begin(int index) {
if (index < 0 || index >= DormNum) {
return false;

View File

@@ -13,15 +13,32 @@ bool asst::InfrastOfficeTask::run()
return false;
}
json::value task_start_json = json::object{
{ "task_type", "InfrastOfficeTask" },
{ "task_chain", m_task_chain}
};
m_callback(AsstMsg::TaskStart, task_start_json, m_callback_arg);
bool ret = swipe_right();
ret &= enter_station({ "Office", "OfficeMini" }, 0);
if (!ret) {
append_task_to_back_to_infrast_home();
return false;
}
json::value enter_json;
enter_json["station"] = "Office";
enter_json["index"] = 0;
m_callback(AsstMsg::EnterStation, enter_json, m_callback_arg);
if (enter_operator_selection()) {
m_callback(AsstMsg::ReadyToShift, enter_json, m_callback_arg);
select_operators(true);
m_callback(AsstMsg::ShiftCompleted, enter_json, m_callback_arg);
}
else {
m_callback(AsstMsg::NoNeedToShift, enter_json, m_callback_arg);
}
m_callback(AsstMsg::TaskCompleted, task_start_json, m_callback_arg);
return true;
}

View File

@@ -13,21 +13,36 @@ bool asst::InfrastPowerTask::run()
return false;
}
json::value task_start_json = json::object{
{ "task_type", "InfrastPowerTask" },
{ "task_chain", m_task_chain}
};
m_callback(AsstMsg::TaskStart, task_start_json, m_callback_arg);
bool is_the_left = false;
for (int i = 0; i != PowerNum; ++i) {
if (need_exit()) {
return false;
}
swipe_left();
enter_station({ "Power", "PowerMini" }, i);
if (!enter_station({ "Power", "PowerMini" }, i)) {
return false;
}
json::value enter_json;
enter_json["station"] = "Power";
enter_json["index"] = i;
m_callback(AsstMsg::EnterStation, enter_json, m_callback_arg);
if (enter_operator_selection()) {
if (is_the_left) {
select_operators(false);
}
else {
select_operators(true);
m_callback(AsstMsg::ReadyToShift, enter_json, m_callback_arg);
select_operators(!is_the_left);
if (!is_the_left) {
is_the_left = true;
}
m_callback(AsstMsg::ShiftCompleted, enter_json, m_callback_arg);
}
else {
m_callback(AsstMsg::NoNeedToShift, enter_json, m_callback_arg);
}
if (!sleep(1000)) {
return false;
@@ -37,6 +52,7 @@ bool asst::InfrastPowerTask::run()
return false;
}
}
m_callback(AsstMsg::TaskCompleted, task_start_json, m_callback_arg);
return true;
}

View File

@@ -25,6 +25,12 @@ bool asst::InfrastProductionTask::run()
return false;
}
json::value task_start_json = json::object{
{ "task_type", "InfrastProductionTask" },
{ "task_chain", m_task_chain}
};
m_callback(AsstMsg::TaskStart, task_start_json, m_callback_arg);
cv::Mat image = m_controller_ptr->get_image();
// 先识别一下有几个制造站/贸易站
const static std::array<std::string, 4> facility_number_key = { "02", "03", "04", "05" };
@@ -50,10 +56,16 @@ bool asst::InfrastProductionTask::run()
sleep(300);
image = m_controller_ptr->get_image();
}
json::value enter_json;
enter_json["station"] = "Production";
enter_json["index"] = i;
m_callback(AsstMsg::EnterStation, enter_json, m_callback_arg);
// 如果当前界面没有添加干员的按钮,那就不换班
auto&& [algorithm1, score1, add_rect1] = m_identify_ptr->find_image(image, "AddOperator");
auto&& [algorithm2, score2, add_rect2] = m_identify_ptr->find_image(image, "AddOperator-Trade");
if (score1 < Configer::TemplThresholdDefault && score2 < Configer::TemplThresholdDefault) {
m_callback(AsstMsg::NoNeedToShift, enter_json, m_callback_arg);
continue;
}
@@ -65,6 +77,11 @@ bool asst::InfrastProductionTask::run()
break;
}
}
enter_json["production"] = m_facility;
m_callback(AsstMsg::StationInfo, enter_json, m_callback_arg);
m_callback(AsstMsg::ReadyToShift, enter_json, m_callback_arg);
//点击添加干员的那个按钮
Rect add_rect = score1 >= score2 ? std::move(add_rect1) : std::move(add_rect2);
m_controller_ptr->click(add_rect);
@@ -82,8 +99,11 @@ bool asst::InfrastProductionTask::run()
auto cur_opers_info = std::move(detect_ret.value());
// TODO可以识别一次把前6个最优解都列出来最多6个制造站然后就不用每次都识别并计算最优解了
std::list<std::string> optimal_comb = calc_optimal_comb(cur_opers_info);
swipe_and_select(optimal_comb);
if (swipe_and_select(optimal_comb)) {
m_callback(AsstMsg::ShiftCompleted, enter_json, m_callback_arg);
}
}
m_callback(AsstMsg::TaskCompleted, task_start_json, m_callback_arg);
return true;
}

View File

@@ -19,7 +19,7 @@ using namespace asst;
WinMacro::WinMacro(const EmulatorInfo& info)
: m_emulator_info(info),
m_rand_engine(std::chrono::system_clock::now().time_since_epoch().count()),
m_screen_filename(GetCurrentDir() + "adb_screen.png")
ScreenFilename(GetCurrentDir() + "adb_screen.png")
{
find_handle();
}
@@ -123,10 +123,10 @@ bool WinMacro::find_handle()
m_emulator_info.adb.screencap = StringReplaceAll(
StringReplaceAll(m_emulator_info.adb.screencap, "[Adb]", adb_dir),
"[Filename]", m_screen_filename);
"[Filename]", ScreenFilename);
m_emulator_info.adb.pullscreen = StringReplaceAll(
StringReplaceAll(m_emulator_info.adb.pullscreen, "[Adb]", adb_dir),
"[Filename]", m_screen_filename);
"[Filename]", ScreenFilename);
DebugTrace("Handle:", m_handle, "Name:", m_emulator_info.name);
return true;
@@ -305,7 +305,7 @@ cv::Mat asst::WinMacro::get_image(bool raw)
if (!call_ret) {
return cv::Mat();
}
cv::Mat raw_mat = cv::imread(m_screen_filename);
cv::Mat raw_mat = cv::imread(ScreenFilename);
if (raw) {
return raw_mat;
}

View File

@@ -33,6 +33,8 @@ namespace asst {
bool swipe(const Rect& r1, const Rect& r2, int duration = 0);
bool swipe_without_scale(const Point& p1, const Point& p2, int duration = 0);
bool swipe_without_scale(const Rect& r1, const Rect& r2, int duration = 0);
const std::string ScreenFilename;
private:
bool find_handle();
std::optional<std::string> call_command(const std::string& cmd, bool use_pipe = true);
@@ -43,7 +45,5 @@ namespace asst {
std::minstd_rand m_rand_engine;
std::pair<int, int> m_scale_size;
double m_control_scale = 1.0;
const std::string m_screen_filename;
};
}

View File

@@ -266,29 +266,39 @@ namespace MeoAsstGui
public enum AsstMsg
{
/* Error Msg */
PtrIsNull,
ImageIsEmpty,
WindowMinimized,
InitFaild,
/* Info Msg */
TaskStart,
ImageFindResult,
ImageMatched,
TaskMatched,
ReachedLimit,
ReadyToSleep,
EndOfSleep,
AppendProcessTask,
AppendTask,
TaskCompleted,
PrintWindow,
TaskStop,
TaskError,
PtrIsNull, // 指针为空
ImageIsEmpty, // 图像为空
WindowMinimized, // [已弃用] 窗口被最小化了
InitFaild, // 初始化失败
TaskError, // 任务错误任务一直出错retry次数达到上限
OcrResultError, // Ocr识别结果错误
/* Info Msg: about Task */
TaskStart = 1000, // 任务开始
TaskMatched, // 任务匹配成功
ReachedLimit, // 单个原子任务达到次数上限
ReadyToSleep, // 准备开始睡眠
EndOfSleep, // 睡眠结束
AppendProcessTask, // 新增流程任务Assistance内部消息外部不需要处理
AppendTask, // 新增任务Assistance内部消息外部不需要处理
TaskCompleted, // 单个原子任务完成
PrintWindow, // 截图消息
TaskStop, // 任务停止
/* Info Msg: about Identify */
TextDetected = 2000, // 识别到文字
ImageFindResult, // 查找图像的结果
ImageMatched, // 图像匹配成功
/* Open Recruit Msg */
TextDetected,
RecruitTagsDetected,
OcrResultError,
RecruitSpecialTag,
RecruitResult
RecruitTagsDetected = 3000, // 公招识别到了Tags
RecruitSpecialTag, // 公招识别到了特殊的Tag
RecruitResult, // 公开招募结果
/* Infrast Msg */
OpersDetected = 4000, // 识别到了干员s
OpersIdtfResult, // 干员识别结果(总的)
InfrastComb, // 当前房间的最优干员组合
EnterStation, // 进入某个房间
StationInfo, // 当前房间信息
ReadyToShift, // 准备换班
ShiftCompleted, // 换班完成(单个房间)
NoNeedToShift // 无需换班(单个房间)
};
}