新增回调消息的处理线程和队列;完成主界面的回调处理

This commit is contained in:
MistEO
2021-08-06 00:48:02 +08:00
parent 345bcfec21
commit dab01fbafd
9 changed files with 168 additions and 75 deletions

View File

@@ -42,7 +42,8 @@ namespace asst {
std::optional<std::string> get_param(const std::string& type, const std::string& param);
private:
static void working_proc(Assistance* pThis);
static void working_proc(Assistance* p_this);
static void msg_proc(Assistance* p_this);
static void task_callback(TaskMsg msg, const json::value& detail, void* custom_arg);
void append_match_task(const std::vector<std::string>& tasks);
@@ -55,15 +56,22 @@ namespace asst {
std::shared_ptr<Identify> m_identify_ptr = nullptr;
bool m_inited = false;
bool m_thread_exit = false;
std::queue<std::shared_ptr<AbstractTask>> m_tasks_queue;
TaskCallback m_callback = nullptr;
void* m_callback_arg = nullptr;
bool m_thread_idle = true;
std::thread m_working_thread;
std::mutex m_mutex;
std::condition_variable m_condvar;
bool m_thread_exit = false;
bool m_thread_idle = true;
std::queue<std::shared_ptr<AbstractTask>> m_tasks_queue;
TaskCallback m_callback = nullptr;
void* m_callback_arg = nullptr;
std::thread m_msg_thread;
std::queue<std::pair<TaskMsg, json::value>> m_msg_queue;
std::mutex m_msg_mutex;
std::condition_variable m_msg_condvar;
};
}

View File

@@ -110,4 +110,5 @@ namespace asst {
#define DebugTrace Logger::get_instance().log_trace
#define DebugTraceInfo Logger::get_instance().log_info
#define DebugTraceError Logger::get_instance().log_error
#define DebugTraceFunction LoggerAux _func_aux(__FUNCTION__)
#define DebugTraceFunction LoggerAux _func_aux(__FUNCTION__)
#define DebugTraceScope LoggerAux _func_aux

View File

@@ -10,6 +10,8 @@
#include "AsstDef.h"
#include "AsstAux.h"
#include <json.h>
namespace cv
{
class Mat;
@@ -39,6 +41,7 @@ namespace asst {
ImageIsEmpty,
WindowMinimized,
/* Info Msg */
TaskStart,
ImageMatched,
TaskMatched,
ReachedLimit,
@@ -47,7 +50,7 @@ namespace asst {
AppendMatchTask,
TaskCompleted,
PrintWindow,
MissionStop,
TaskStop,
/* Open Recruit Msg */
TextDetected,
RecruitTagsDetected,
@@ -62,6 +65,7 @@ namespace asst {
{TaskMsg::PtrIsNull, "PtrIsNull"},
{TaskMsg::ImageIsEmpty, "ImageIsEmpty"},
{TaskMsg::WindowMinimized, "WindowMinimized"},
{TaskMsg::TaskStart, "TaskStart"},
{TaskMsg::ImageMatched, "ImageMatched"},
{TaskMsg::TaskMatched, "TaskMatched"},
{TaskMsg::ReachedLimit, "ReachedLimit"},
@@ -70,7 +74,7 @@ namespace asst {
{TaskMsg::AppendMatchTask, "AppendMatchTask"},
{TaskMsg::TaskCompleted, "TaskCompleted"},
{TaskMsg::PrintWindow, "PrintWindow"},
{TaskMsg::MissionStop, "MissionStop"},
{TaskMsg::TaskStop, "TaskStop"},
{TaskMsg::TextDetected, "TextDetected"},
{TaskMsg::RecruitTagsDetected, "RecruitTagsDetected"},
{TaskMsg::OcrResultError, "OcrResultError"},

View File

@@ -35,6 +35,7 @@ Assistance::Assistance(TaskCallback callback, void* callback_arg)
m_identify_ptr->ocr_init_models(GetResourceDir() + "OcrLiteNcnn\\models\\");
m_working_thread = std::thread(working_proc, this);
m_msg_thread = std::thread(msg_proc, this);
}
Assistance::~Assistance()
@@ -48,10 +49,14 @@ Assistance::~Assistance()
m_thread_exit = true;
m_thread_idle = true;
m_condvar.notify_all();
m_msg_condvar.notify_all();
if (m_working_thread.joinable()) {
m_working_thread.join();
}
if (m_msg_thread.joinable()) {
m_msg_thread.join();
}
}
std::optional<std::string> Assistance::catch_emulator(const std::string& emulator_name)
@@ -184,23 +189,24 @@ std::optional<std::string> Assistance::get_param(const std::string& type, const
return Configer::get_instance().get_param(type, param);
}
void Assistance::working_proc(Assistance* pThis)
void Assistance::working_proc(Assistance* p_this)
{
DebugTraceFunction;
int retry_times = 0;
while (!pThis->m_thread_exit) {
std::unique_lock<std::mutex> lock(pThis->m_mutex);
if (!pThis->m_thread_idle && !pThis->m_tasks_queue.empty()) {
while (!p_this->m_thread_exit) {
DebugTraceScope("Assistance::working_proc Loop");
std::unique_lock<std::mutex> lock(p_this->m_mutex);
if (!p_this->m_thread_idle && !p_this->m_tasks_queue.empty()) {
auto start_time = std::chrono::system_clock::now();
std::shared_ptr<AbstractTask> task_ptr = pThis->m_tasks_queue.front();
task_ptr->set_ptr(pThis->m_window_ptr, pThis->m_view_ptr, pThis->m_control_ptr, pThis->m_identify_ptr);
task_ptr->set_exit_flag(&pThis->m_thread_idle);
std::shared_ptr<AbstractTask> task_ptr = p_this->m_tasks_queue.front();
task_ptr->set_ptr(p_this->m_window_ptr, p_this->m_view_ptr, p_this->m_control_ptr, p_this->m_identify_ptr);
task_ptr->set_exit_flag(&p_this->m_thread_idle);
bool ret = task_ptr->run();
if (ret) {
retry_times = 0;
pThis->m_tasks_queue.pop();
p_this->m_tasks_queue.pop();
}
else // 失败了不pop一直跑。 Todo: 设一个上限
{
@@ -210,8 +216,8 @@ void Assistance::working_proc(Assistance* pThis)
// 如果下个任务是识别,就按识别的延时来;如果下个任务是点击,就按点击的延时来;……
// 如果都符合,就取个最大值
int delay = 0;
if (!pThis->m_tasks_queue.empty()) {
int next_type = pThis->m_tasks_queue.front()->get_task_type();
if (!p_this->m_tasks_queue.empty()) {
int next_type = p_this->m_tasks_queue.front()->get_task_type();
std::vector<int> candidate_delay = { 0 };
if (next_type & TaskType::TaskTypeClick) {
candidate_delay.emplace_back(Configer::get_instance().m_options.task_control_delay);
@@ -221,21 +227,45 @@ void Assistance::working_proc(Assistance* pThis)
}
delay = *std::max_element(candidate_delay.cbegin(), candidate_delay.cend());
}
pThis->m_condvar.wait_until(lock,
p_this->m_condvar.wait_until(lock,
start_time + std::chrono::milliseconds(delay),
[&]() -> bool { return pThis->m_thread_idle; });
[&]() -> bool { return p_this->m_thread_idle; });
}
else {
pThis->m_thread_idle = true;
pThis->m_condvar.wait(lock);
p_this->m_thread_idle = true;
p_this->m_condvar.wait(lock);
}
}
}
void Assistance::msg_proc(Assistance* p_this)
{
DebugTraceFunction;
while (!p_this->m_thread_exit) {
DebugTraceScope("Assistance::msg_proc Loop");
std::unique_lock<std::mutex> lock(p_this->m_msg_mutex);
if (!p_this->m_msg_queue.empty()) {
// 结构化绑定只能是引用后续的pop会使引用失效所以需要重新构造一份这里采用了move的方式
auto && [temp_msg, temp_detail] = p_this->m_msg_queue.front();
TaskMsg msg = std::move(temp_msg);
json::value detail = std::move(temp_detail);
p_this->m_msg_queue.pop();
lock.unlock();
if (p_this->m_callback) {
p_this->m_callback(msg, detail, p_this->m_callback_arg);
}
}
else {
p_this->m_msg_condvar.wait(lock);
}
}
}
void Assistance::task_callback(TaskMsg msg, const json::value& detail, void* custom_arg)
{
DebugTraceFunction;
DebugTrace(msg, detail.to_string());
DebugTrace("Assistance::task_callback |", msg, detail.to_string());
Assistance* p_this = (Assistance*)custom_arg;
json::value more_detail = detail;
@@ -259,7 +289,9 @@ void Assistance::task_callback(TaskMsg msg, const json::value& detail, void* cus
}
if (p_this->m_callback) {
p_this->m_callback(msg, more_detail, p_this->m_callback_arg);
std::unique_lock<std::mutex> lock(p_this->m_msg_mutex);
p_this->m_msg_queue.emplace(msg, std::move(more_detail));
p_this->m_msg_condvar.notify_one();
}
}

View File

@@ -140,6 +140,8 @@ bool MatchTask::run()
return false;
}
m_callback(TaskMsg::TaskStart, json::value(), m_callback_arg);
Rect rect;
auto&& ret = match_image(&rect);
if (!ret) {
@@ -179,7 +181,7 @@ bool MatchTask::run()
case MatchTaskType::DoNothing:
break;
case MatchTaskType::Stop:
m_callback(TaskMsg::MissionStop, json::value(), m_callback_arg);
m_callback(TaskMsg::TaskStop, json::value(), m_callback_arg);
need_stop = true;
break;
case MatchTaskType::PrintWindow:
@@ -206,12 +208,12 @@ bool MatchTask::run()
return true;
}
// 后置固定延时
sleep(task.rear_delay);
callback_json["exec_times"] = task.exec_times;
m_callback(TaskMsg::TaskCompleted, callback_json, m_callback_arg);
// 后置固定延时
sleep(task.rear_delay);
json::value next_json = callback_json;
next_json["tasks"] = json::array(task.next);
m_callback(TaskMsg::AppendMatchTask, next_json, m_callback_arg);
@@ -314,6 +316,8 @@ bool OpenRecruitTask::run()
return false;
}
m_callback(TaskMsg::TaskStart, json::value(), m_callback_arg);
/* Find all text */
std::vector<TextArea> all_text_area = ocr_detect();
@@ -515,6 +519,8 @@ bool ClickTask::run()
m_callback(TaskMsg::PtrIsNull, json::value(), m_callback_arg);
return false;
}
m_callback(TaskMsg::TaskStart, json::value(), m_callback_arg);
m_control_ptr->click(m_rect);
return true;
}

View File

@@ -4,6 +4,13 @@
xmlns:local="clr-namespace:MeoAsstGui"
StartupUri="MainWindow.xaml">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/MaterialDesignTheme.Light.xaml"/>
<ResourceDictionary Source="pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/MaterialDesignTheme.Defaults.xaml"/>
<ResourceDictionary Source="pack://application:,,,/MaterialDesignColors;component/Themes/Recommended/Primary/MaterialDesignColor.Blue.xaml"/>
<ResourceDictionary Source="pack://application:,,,/MaterialDesignColors;component/Themes/Recommended/Accent/MaterialDesignColor.Indigo.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>

View File

@@ -14,6 +14,9 @@ using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Runtime.InteropServices;
using System.Windows.Threading;
using System.Threading;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace MeoAsstGui
{
@@ -35,20 +38,81 @@ namespace MeoAsstGui
IntPtr p_asst, string type, string param,
[In, Out] StringBuilder lp_string, int buffer_size);
private delegate void CallbackDelegate(int msg, IntPtr json_buffer, IntPtr custom_arg);
private delegate void ProcCallbckMsg(TaskMsg msg, JObject detail);
private static CallbackDelegate callback;
private IntPtr p_asst;
private UpdateDialog updateDialog;
private RecruitWindow recuitWindow;
private DispatcherTimer update_times = new DispatcherTimer();
public enum TaskMsg
{
/* Error Msg */
PtrIsNull,
ImageIsEmpty,
WindowMinimized,
/* Info Msg */
TaskStart,
ImageMatched,
TaskMatched,
ReachedLimit,
ReadyToSleep,
EndOfSleep,
AppendMatchTask,
TaskCompleted,
PrintWindow,
TaskStop,
/* Open Recruit Msg */
TextDetected,
RecruitTagsDetected,
OcrResultError,
RecruitSpecialTag,
RecruitResult,
AppendTask
};
private void CallbackFunction(int msg, IntPtr json_buffer, IntPtr custom_arg)
{
string json_str = Marshal.PtrToStringAnsi(json_buffer);
Console.WriteLine(json_str);
//Console.WriteLine(json_str);
JObject json = (JObject)JsonConvert.DeserializeObject(json_str);
ProcCallbckMsg dlg = new ProcCallbckMsg(updateGui);
this.Dispatcher.Invoke(dlg, msg, json);
}
private void updateGui(TaskMsg msg, JObject detail)
{
switch (msg)
{
case TaskMsg.TaskCompleted:
string task_name = detail["name"].ToString();
if (task_name == "StartButton2")
{
exec_times.Content = "已开始行动 " + (int)detail["exec_times"] + " 次";
}
else if (task_name == "StoneConfirm")
{
stone_times.Content = "已碎石 " + (int)detail["exec_times"] + " 个";
}
break;
case TaskMsg.TaskStart:
label_status.Content = "正在运行中……";
break;
case TaskMsg.TaskStop:
label_status.Content = "已刷完,自动停止";
if (checkBox_shutdown.IsChecked == true)
{
System.Diagnostics.Process.Start("shutdown.exe", "-s -t 60");
MessageBoxResult result = MessageBox.Show("已刷完,即将关机,是否取消?", "提示", MessageBoxButton.OK);
if (result == MessageBoxResult.OK)
{
System.Diagnostics.Process.Start("shutdown.exe", "-a");
}
}
break;
}
}
public MainWindow()
{
@@ -63,8 +127,8 @@ namespace MeoAsstGui
{
callback = CallbackFunction;
p_asst = AsstCreateEx(callback, IntPtr.Zero);
update_times.Tick += new EventHandler(updateExecTimes);
update_times.Interval = TimeSpan.FromSeconds(1);
//update_times.Tick += new EventHandler(updateExecTimes);
//update_times.Interval = TimeSpan.FromSeconds(1);
// for debug
//updateDialog = new UpdateDialog();
@@ -76,14 +140,12 @@ namespace MeoAsstGui
bool catched = AsstCatchEmulator(p_asst);
catch_status.Content = "捕获模拟器窗口:" + catched;
AsstStart(p_asst, "SanityBegin");
update_times.Start();
}
private void button_Click_stop(object sender, RoutedEventArgs e)
{
AsstStop(p_asst);
catch_status.Content = "";
update_times.Stop();
exec_times.Content = "";
stone_times.Content = "";
label_status.Content = "";
@@ -132,43 +194,6 @@ namespace MeoAsstGui
AsstStart(p_asst, "VisitBegin");
}
private void updateExecTimes(object sender, EventArgs e)
{
StringBuilder buff_start = new StringBuilder(16);
AsstGetParam(p_asst, "task.execTimes", "StartButton2", buff_start, 16);
exec_times.Content = "已开始行动 " + buff_start + " 次";
if (checkBox_useStone.IsChecked == true)
{
StringBuilder buff_stone = new StringBuilder(16);
AsstGetParam(p_asst, "task.execTimes", "StoneConfirm", buff_stone, 16);
stone_times.Content = "已碎石 " + buff_stone + " 个";
}
StringBuilder buff_running = new StringBuilder(4);
AsstGetParam(p_asst, "status", "running", buff_running, 4);
if (int.Parse(buff_running.ToString()) == 0)
{
update_times.Stop();
label_status.Content = "已刷完,自动停止";
if (checkBox_shutdown.IsChecked == true)
{
System.Diagnostics.Process.Start("shutdown.exe", "-s -t 60");
MessageBoxResult result = MessageBox.Show("已刷完,即将关机,是否取消?", "提示", MessageBoxButton.OK);
if (result == MessageBoxResult.OK)
{
System.Diagnostics.Process.Start("shutdown.exe", "-a");
}
}
}
else
{
label_status.Content = "正在运行中……";
}
}
private void checkBox_maxTimes_Checked(object sender, RoutedEventArgs e)
{
if (checkBox_maxTimes.IsChecked == true)

View File

@@ -59,6 +59,9 @@
<StartupObject>MeoAsstGui.App</StartupObject>
</PropertyGroup>
<ItemGroup>
<Reference Include="Newtonsoft.Json, Version=13.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\packages\Newtonsoft.Json.13.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
@@ -124,6 +127,7 @@
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
</EmbeddedResource>
<None Include="packages.config" />
<None Include="Properties\Settings.settings">
<Generator>SettingsSingleFileGenerator</Generator>
<LastGenOutput>Settings.Designer.cs</LastGenOutput>

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="MaterialDesignColors" version="2.0.1" targetFramework="net472" />
<package id="MaterialDesignThemes" version="4.1.0" targetFramework="net472" />
<package id="Newtonsoft.Json" version="13.0.1" targetFramework="net472" />
</packages>