mirror of
https://github.com/MaaAssistantArknights/MaaAssistantArknights.git
synced 2026-07-17 18:01:26 +08:00
Revert "feat: 重构 TaskData (#7410)"
This reverts commit782e71d4aa, reversing changes made to940008b448.
This commit is contained in:
@@ -335,106 +335,53 @@ namespace asst
|
||||
}
|
||||
return "Invalid";
|
||||
}
|
||||
|
||||
enum class TaskDerivedType
|
||||
{
|
||||
Invalid = -1,
|
||||
Raw, // 原始任务
|
||||
BaseTask, // BaseTask
|
||||
Implicit, // 隐式 TemplateTask
|
||||
Template, // 显式 TemplateTask
|
||||
};
|
||||
|
||||
inline std::string enum_to_string(TaskDerivedType task_derived_type)
|
||||
{
|
||||
static const std::unordered_map<TaskDerivedType, std::string> task_derived_type_map = {
|
||||
{ TaskDerivedType::Raw, "Raw" },
|
||||
{ TaskDerivedType::BaseTask, "BaseTask" },
|
||||
{ TaskDerivedType::Implicit, "Implicit" },
|
||||
{ TaskDerivedType::Template, "Template" },
|
||||
};
|
||||
if (auto it = task_derived_type_map.find(task_derived_type); it != task_derived_type_map.end()) {
|
||||
return it->second;
|
||||
}
|
||||
return "Invalid";
|
||||
}
|
||||
} // namespace asst
|
||||
|
||||
namespace asst
|
||||
{
|
||||
using TaskList = std::vector<std::string>;
|
||||
|
||||
// 任务流程信息
|
||||
struct TaskPipelineInfo
|
||||
{
|
||||
constexpr TaskPipelineInfo() = default;
|
||||
constexpr virtual ~TaskPipelineInfo() = default;
|
||||
constexpr TaskPipelineInfo(const TaskPipelineInfo&) = default;
|
||||
constexpr TaskPipelineInfo(TaskPipelineInfo&&) noexcept = default;
|
||||
constexpr TaskPipelineInfo& operator=(const TaskPipelineInfo&) = default;
|
||||
constexpr TaskPipelineInfo& operator=(TaskPipelineInfo&&) noexcept = default;
|
||||
std::string name; // 任务名
|
||||
TaskList next; // 下一个可能的任务(列表)
|
||||
TaskList sub; // 子任务(列表)
|
||||
TaskList on_error_next; // 任务出错之后要去执行什么
|
||||
TaskList exceeded_next; // 达到最多次数了之后,下一个可能的任务(列表)
|
||||
TaskList reduce_other_times; // 执行了该任务后,需要减少别的任务的执行次数。例如执行了吃理智药,
|
||||
// 则说明上一次点击蓝色开始行动按钮没生效,所以蓝色开始行动要-1
|
||||
};
|
||||
using TaskPipelinePtr = std::shared_ptr<TaskPipelineInfo>;
|
||||
using TaskPipelineConstPtr = std::shared_ptr<const TaskPipelineInfo>;
|
||||
|
||||
// 任务继承信息
|
||||
struct TaskDerivedInfo : public TaskPipelineInfo
|
||||
{
|
||||
constexpr TaskDerivedInfo() = default;
|
||||
constexpr virtual ~TaskDerivedInfo() = default;
|
||||
constexpr TaskDerivedInfo(const TaskDerivedInfo&) = default;
|
||||
constexpr TaskDerivedInfo(TaskDerivedInfo&&) noexcept = default;
|
||||
constexpr TaskDerivedInfo& operator=(const TaskDerivedInfo&) = default;
|
||||
constexpr TaskDerivedInfo& operator=(TaskDerivedInfo&&) noexcept = default;
|
||||
TaskDerivedType type = TaskDerivedType::Raw; // 任务类型
|
||||
std::string base = ""; // 继承自哪个任务(Raw 任务不需要)
|
||||
std::string prefix = ""; // 需要添加的前缀(仅对 TemplateTask 生效)
|
||||
};
|
||||
using TaskDerivedPtr = std::shared_ptr<TaskDerivedInfo>;
|
||||
using TaskDerivedConstPtr = std::shared_ptr<const TaskDerivedInfo>;
|
||||
|
||||
// 任务信息
|
||||
struct TaskInfo : public TaskPipelineInfo
|
||||
struct TaskInfo
|
||||
{
|
||||
constexpr TaskInfo() = default;
|
||||
constexpr virtual ~TaskInfo() = default;
|
||||
constexpr TaskInfo(const TaskInfo&) = default;
|
||||
constexpr TaskInfo(TaskInfo&&) noexcept = default;
|
||||
constexpr TaskInfo& operator=(const TaskInfo&) = default;
|
||||
constexpr TaskInfo& operator=(TaskInfo&&) noexcept = default;
|
||||
AlgorithmType algorithm = AlgorithmType::Invalid; // 图像算法类型
|
||||
ProcessTaskAction action = ProcessTaskAction::Invalid; // 要进行的操作
|
||||
bool sub_error_ignored = false; // 子任务如果失败了,是否继续执行剩下的任务
|
||||
int max_times = INT_MAX; // 任务最多执行多少次
|
||||
Rect specific_rect; // 指定区域,目前仅针对ClickRect任务有用,会点这个区域
|
||||
int pre_delay = 0; // 执行该任务前的延时
|
||||
int post_delay = 0; // 执行该任务后的延时
|
||||
int retry_times = INT_MAX; // 未找到图像时的重试次数
|
||||
Rect roi; // 要识别的区域,若为0则全图识别
|
||||
TaskInfo() = default;
|
||||
virtual ~TaskInfo() = default;
|
||||
TaskInfo(const TaskInfo&) = default;
|
||||
TaskInfo(TaskInfo&&) noexcept = default;
|
||||
TaskInfo& operator=(const TaskInfo&) = default;
|
||||
TaskInfo& operator=(TaskInfo&&) noexcept = default;
|
||||
std::string name; // 任务名
|
||||
AlgorithmType algorithm = // 图像算法类型
|
||||
AlgorithmType::Invalid;
|
||||
ProcessTaskAction action = // 要进行的操作
|
||||
ProcessTaskAction::Invalid;
|
||||
std::vector<std::string> sub; // 子任务(列表)
|
||||
bool sub_error_ignored = false; // 子任务如果失败了,是否继续执行剩下的任务
|
||||
std::vector<std::string> next; // 下一个可能的任务(列表)
|
||||
int max_times = INT_MAX; // 任务最多执行多少次
|
||||
std::vector<std::string> exceeded_next; // 达到最多次数了之后,下一个可能的任务(列表)
|
||||
std::vector<std::string> on_error_next; // 任务出错之后要去执行什么
|
||||
std::vector<std::string> reduce_other_times; // 执行了该任务后,需要减少别的任务的执行次数。
|
||||
// 例如执行了吃理智药,则说明上一次点击蓝色开始行动按钮没生效,
|
||||
// 所以蓝色开始行动要-1
|
||||
Rect specific_rect; // 指定区域,目前仅针对ClickRect任务有用,会点这个区域
|
||||
int pre_delay = 0; // 执行该任务前的延时
|
||||
int post_delay = 0; // 执行该任务后的延时
|
||||
int retry_times = INT_MAX; // 未找到图像时的重试次数
|
||||
Rect roi; // 要识别的区域,若为0则全图识别
|
||||
Rect rect_move; // 识别结果移动:有些结果识别到的,和要点击的不是同一个位置。
|
||||
// 即识别到了res,点击res + result_move的位置
|
||||
bool cache = false; // 是否使用缓存区域
|
||||
std::vector<int> special_params; // 某些任务会用到的特殊参数
|
||||
};
|
||||
using TaskPtr = std::shared_ptr<TaskInfo>;
|
||||
using TaskConstPtr = std::shared_ptr<const TaskInfo>;
|
||||
|
||||
// 文字识别任务的信息
|
||||
struct OcrTaskInfo : public TaskInfo
|
||||
{
|
||||
constexpr OcrTaskInfo() = default;
|
||||
constexpr virtual ~OcrTaskInfo() override = default;
|
||||
constexpr OcrTaskInfo(const OcrTaskInfo&) = default;
|
||||
constexpr OcrTaskInfo(OcrTaskInfo&&) noexcept = default;
|
||||
constexpr OcrTaskInfo& operator=(const OcrTaskInfo&) = default;
|
||||
constexpr OcrTaskInfo& operator=(OcrTaskInfo&&) noexcept = default;
|
||||
OcrTaskInfo() = default;
|
||||
virtual ~OcrTaskInfo() override = default;
|
||||
OcrTaskInfo(const OcrTaskInfo&) = default;
|
||||
OcrTaskInfo(OcrTaskInfo&&) noexcept = default;
|
||||
OcrTaskInfo& operator=(const OcrTaskInfo&) = default;
|
||||
OcrTaskInfo& operator=(OcrTaskInfo&&) noexcept = default;
|
||||
std::vector<std::string> text; // 文字的容器,匹配到这里面任一个,就算匹配上了
|
||||
bool full_match = false; // 是否需要全匹配,否则搜索到子串就算匹配上了
|
||||
bool is_ascii = false; // 是否启用字符数字模型
|
||||
@@ -443,41 +390,35 @@ namespace asst
|
||||
std::vector<std::pair<std::string, std::string>>
|
||||
replace_map; // 部分文字容易识别错,字符串强制replace之后,再进行匹配
|
||||
};
|
||||
using OcrTaskPtr = std::shared_ptr<OcrTaskInfo>;
|
||||
using OcrTaskConstPtr = std::shared_ptr<const OcrTaskInfo>;
|
||||
|
||||
// 图片匹配任务的信息
|
||||
struct MatchTaskInfo : public TaskInfo
|
||||
{
|
||||
constexpr MatchTaskInfo() = default;
|
||||
constexpr virtual ~MatchTaskInfo() override = default;
|
||||
constexpr MatchTaskInfo(const MatchTaskInfo&) = default;
|
||||
constexpr MatchTaskInfo(MatchTaskInfo&&) noexcept = default;
|
||||
constexpr MatchTaskInfo& operator=(const MatchTaskInfo&) = default;
|
||||
constexpr MatchTaskInfo& operator=(MatchTaskInfo&&) noexcept = default;
|
||||
MatchTaskInfo() = default;
|
||||
virtual ~MatchTaskInfo() override = default;
|
||||
MatchTaskInfo(const MatchTaskInfo&) = default;
|
||||
MatchTaskInfo(MatchTaskInfo&&) noexcept = default;
|
||||
MatchTaskInfo& operator=(const MatchTaskInfo&) = default;
|
||||
MatchTaskInfo& operator=(MatchTaskInfo&&) noexcept = default;
|
||||
std::vector<std::string> templ_names; // 匹配模板图片文件名
|
||||
std::vector<double> templ_thresholds; // 模板匹配阈值
|
||||
std::pair<int, int> mask_range; // 掩码的二值化范围
|
||||
};
|
||||
using MatchTaskPtr = std::shared_ptr<MatchTaskInfo>;
|
||||
using MatchTaskConstPtr = std::shared_ptr<const MatchTaskInfo>;
|
||||
|
||||
// hash 计算任务的信息
|
||||
struct HashTaskInfo : public TaskInfo
|
||||
{
|
||||
constexpr HashTaskInfo() = default;
|
||||
constexpr virtual ~HashTaskInfo() override = default;
|
||||
constexpr HashTaskInfo(const HashTaskInfo&) = default;
|
||||
constexpr HashTaskInfo(HashTaskInfo&&) noexcept = default;
|
||||
constexpr HashTaskInfo& operator=(const HashTaskInfo&) = default;
|
||||
constexpr HashTaskInfo& operator=(HashTaskInfo&&) noexcept = default;
|
||||
HashTaskInfo() = default;
|
||||
virtual ~HashTaskInfo() override = default;
|
||||
HashTaskInfo(const HashTaskInfo&) = default;
|
||||
HashTaskInfo(HashTaskInfo&&) noexcept = default;
|
||||
HashTaskInfo& operator=(const HashTaskInfo&) = default;
|
||||
HashTaskInfo& operator=(HashTaskInfo&&) noexcept = default;
|
||||
std::vector<std::string> hashes; // 需要多个哈希值
|
||||
int dist_threshold = 0; // 汉明距离阈值
|
||||
std::pair<int, int> mask_range; // 掩码的二值化范围
|
||||
bool bound = false; // 是否裁剪周围黑边
|
||||
};
|
||||
using HashTaskPtr = std::shared_ptr<HashTaskInfo>;
|
||||
using HashTaskConstPtr = std::shared_ptr<const HashTaskInfo>;
|
||||
|
||||
inline static const std::string UploadDataSource = "MeoAssistant";
|
||||
} // namespace asst
|
||||
|
||||
@@ -9,8 +9,6 @@
|
||||
|
||||
#include "Common/AsstTypes.h"
|
||||
#include "GeneralConfig.h"
|
||||
#include "TaskData/TaskDataSymbolStream.h"
|
||||
#include "TaskData/TaskDataTypes.h"
|
||||
#include "TemplResource.h"
|
||||
#include "Utils/JsonMisc.hpp"
|
||||
#include "Utils/Logger.hpp"
|
||||
@@ -21,9 +19,9 @@ const std::unordered_set<std::string>& asst::TaskData::get_templ_required() cons
|
||||
{
|
||||
return m_templ_required;
|
||||
}
|
||||
asst::TaskDerivedConstPtr asst::TaskData::get_raw(std::string_view name)
|
||||
asst::TaskData::taskptr_t asst::TaskData::get_raw(std::string_view name)
|
||||
{
|
||||
if (!generate_raw_task_and_base(name, true)) [[unlikely]] {
|
||||
if (!generate_task_and_its_base(name, true)) [[unlikely]] {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -37,27 +35,32 @@ asst::TaskDerivedConstPtr asst::TaskData::get_raw(std::string_view name)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// 隐式 TemplateTask
|
||||
if (auto base_task_iter = get_raw(name.substr(name_len + 1))) {
|
||||
return _generate_task_info(base_task_iter, name.substr(0, name_len));
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
asst::TaskPtr asst::TaskData::get(std::string_view name)
|
||||
asst::TaskData::taskptr_t asst::TaskData::get(std::string_view name)
|
||||
{
|
||||
// 普通任务 或 已经生成过的高级任务
|
||||
if (auto it = m_all_tasks_info.find(name); it != m_all_tasks_info.cend()) {
|
||||
if (auto it = m_all_tasks_info.find(name); it != m_all_tasks_info.cend()) [[likely]] {
|
||||
return it->second;
|
||||
}
|
||||
|
||||
auto task = generate_task_info(name);
|
||||
auto raw_task = get_raw(name);
|
||||
auto task = _generate_task_info(raw_task);
|
||||
if (!task) [[unlikely]] {
|
||||
return nullptr;
|
||||
}
|
||||
#define ASST_TASKDATA_GET_IF_BRANCH(list, m) \
|
||||
if (auto opt = compile_tasklist(task->list, name, m); !opt) [[unlikely]] { \
|
||||
Log.error("Generate task_list", std::string(name) + "->" #list, "failed.", opt.error()); \
|
||||
return nullptr; \
|
||||
} \
|
||||
else { \
|
||||
task->list = std::move(opt.value().tasks); \
|
||||
bool changed = false; // 任务列表是否发生变化(是否包含虚任务)
|
||||
#define ASST_TASKDATA_GET_IF_BRANCH(list, m) \
|
||||
task->list.clear(); \
|
||||
if (!compile_tasklist(task->list, raw_task->list, name, changed, m)) [[unlikely]] { \
|
||||
Log.error("Generate task_list", std::string(name) + "->" #list, "failed."); \
|
||||
return nullptr; \
|
||||
}
|
||||
// 展开五个任务列表中的虚任务
|
||||
ASST_TASKDATA_GET_IF_BRANCH(next, false);
|
||||
@@ -70,12 +73,12 @@ asst::TaskPtr asst::TaskData::get(std::string_view name)
|
||||
constexpr size_t MAX_TASKS_SIZE = 65535;
|
||||
if (m_all_tasks_info.size() < MAX_TASKS_SIZE) [[likely]] {
|
||||
// 保存最终生成的任务,下次查询时可以直接返回
|
||||
return insert_or_assign_task(name, task).first->second;
|
||||
return insert_or_assign_task(name, changed ? task : raw_task).first->second;
|
||||
}
|
||||
else {
|
||||
// 个数超过上限时不保存,直接返回,防止内存占用过大
|
||||
Log.warn("Task count has exceeded the upper limit:", MAX_TASKS_SIZE, "current task:", name);
|
||||
return task;
|
||||
return changed ? task : raw_task;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -141,7 +144,7 @@ bool asst::TaskData::lazy_parse(const json::value& json)
|
||||
validity = false;
|
||||
continue;
|
||||
}
|
||||
auto check_tasklist = [&](const TaskList& task_list, std::string_view list_type,
|
||||
auto check_tasklist = [&](const tasklist_t& task_list, std::string_view list_type,
|
||||
bool enable_justreturn_check = false) {
|
||||
std::unordered_set<std::string_view> tasks_set {};
|
||||
std::string justreturn_task_name = "";
|
||||
@@ -157,7 +160,7 @@ bool asst::TaskData::lazy_parse(const json::value& json)
|
||||
validity = false;
|
||||
}
|
||||
|
||||
if (auto ptr = get(task_name); ptr == nullptr) [[unlikely]] {
|
||||
if (auto ptr = get_raw(task_name); ptr == nullptr) [[unlikely]] {
|
||||
Log.error(task_name, "in", (std::string(name) += "->") += list_type, "is null");
|
||||
validity = false;
|
||||
continue;
|
||||
@@ -209,7 +212,7 @@ bool asst::TaskData::parse(const json::value& json)
|
||||
|
||||
// 本来重构之后完全支持惰性加载,但是发现模板图片不支持(
|
||||
for (std::string_view name : m_json_all_tasks_info | views::keys) {
|
||||
generate_task_info(name);
|
||||
generate_task_and_its_base(name, true);
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -232,37 +235,19 @@ void asst::TaskData::set_task_base(const std::string_view task_name, std::string
|
||||
clear_tasks();
|
||||
}
|
||||
|
||||
bool asst::TaskData::generate_raw_task_info(std::string_view name, std::string_view prefix, std::string_view base,
|
||||
const json::value& json, TaskDerivedType type)
|
||||
{
|
||||
TaskPipelineConstPtr base_task = base.empty() ? nullptr : get_raw(base);
|
||||
if (base_task == nullptr) {
|
||||
base_task = default_task_info_ptr;
|
||||
prefix = "";
|
||||
}
|
||||
|
||||
TaskDerivedPtr task = std::make_shared<TaskDerivedInfo>();
|
||||
task->name = name;
|
||||
task->type = type;
|
||||
task->base = base;
|
||||
task->prefix = prefix;
|
||||
utils::get_value_or(name, json, "next", task->next, [&]() { return append_prefix(base_task->next, prefix); });
|
||||
utils::get_value_or(name, json, "sub", task->sub, [&]() { return append_prefix(base_task->sub, prefix); });
|
||||
utils::get_value_or(name, json, "exceededNext", task->exceeded_next,
|
||||
[&]() { return append_prefix(base_task->exceeded_next, prefix); });
|
||||
utils::get_value_or(name, json, "onErrorNext", task->on_error_next,
|
||||
[&]() { return append_prefix(base_task->on_error_next, prefix); });
|
||||
utils::get_value_or(name, json, "reduceOtherTimes", task->reduce_other_times,
|
||||
[&]() { return append_prefix(base_task->reduce_other_times, prefix); });
|
||||
m_task_status[task_name_view(name)] = NotToBeGenerate;
|
||||
|
||||
// 保存虚任务未展开的任务
|
||||
insert_or_assign_raw_task(name, task);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool asst::TaskData::generate_raw_task_and_base(std::string_view name, bool must_true)
|
||||
bool asst::TaskData::generate_task_and_its_base(std::string_view name, bool must_true)
|
||||
{
|
||||
auto generate_task = [&](std::string_view name, std::string_view prefix, taskptr_t base_ptr,
|
||||
const json::value& task_json) {
|
||||
auto task_info_ptr = generate_task_info(name, task_json, base_ptr, prefix);
|
||||
if (task_info_ptr == nullptr) {
|
||||
return false;
|
||||
}
|
||||
m_task_status[task_name_view(name)] = NotToBeGenerate;
|
||||
// 保存虚任务未展开的任务
|
||||
insert_or_assign_raw_task(name, task_info_ptr);
|
||||
return true;
|
||||
};
|
||||
switch (m_task_status[task_name_view(name)]) {
|
||||
case NotToBeGenerate:
|
||||
// 已经显式生成
|
||||
@@ -272,12 +257,7 @@ bool asst::TaskData::generate_raw_task_and_base(std::string_view name, bool must
|
||||
|
||||
// 隐式生成的资源
|
||||
if (size_t p = name.find('@'); p != std::string::npos) {
|
||||
if (generate_raw_task_and_base(name.substr(p + 1), must_true)) {
|
||||
// 隐式 TemplateTask
|
||||
generate_raw_task_info(name, name.substr(0, p), name.substr(p + 1), {}, TaskDerivedType::Implicit);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
return generate_task_and_its_base(name.substr(p + 1), must_true);
|
||||
}
|
||||
|
||||
m_task_status[name] = NotExists;
|
||||
@@ -303,17 +283,22 @@ bool asst::TaskData::generate_raw_task_and_base(std::string_view name, bool must
|
||||
// BaseTask
|
||||
if (auto opt = task_json.find<std::string>("baseTask")) {
|
||||
std::string base = opt.value();
|
||||
return generate_raw_task_and_base(base, must_true) &&
|
||||
generate_raw_task_info(name, "", base, task_json, TaskDerivedType::BaseTask);
|
||||
return generate_task_and_its_base(base, must_true) && generate_task(name, "", get_raw(base), task_json);
|
||||
}
|
||||
|
||||
// TemplateTask
|
||||
if (size_t p = name.find('@'); p != std::string::npos) {
|
||||
if (std::string_view base = name.substr(p + 1); generate_raw_task_and_base(base, false)) {
|
||||
return generate_raw_task_info(name, name.substr(0, p), base, task_json, TaskDerivedType::Template);
|
||||
if (std::string_view base = name.substr(p + 1); generate_task_and_its_base(base, false)) {
|
||||
#ifdef ASST_DEBUG
|
||||
if (task_json.as_object().empty() && get_raw(base)->algorithm != AlgorithmType::MatchTemplate) {
|
||||
// 多余的空任务
|
||||
Log.warn("Task", name, "is a redundant empty task because it is not MatchTemplate");
|
||||
}
|
||||
#endif
|
||||
return generate_task(name, name.substr(0, p), get_raw(base), task_json);
|
||||
}
|
||||
}
|
||||
return generate_raw_task_info(name, "", "", task_json, TaskDerivedType::Raw);
|
||||
return generate_task(name, "", nullptr, task_json);
|
||||
}
|
||||
[[unlikely]] case Generating:
|
||||
Log.error("Task", name, "is generated cyclically");
|
||||
@@ -324,123 +309,59 @@ bool asst::TaskData::generate_raw_task_and_base(std::string_view name, bool must
|
||||
}
|
||||
}
|
||||
|
||||
asst::TaskPtr asst::TaskData::generate_task_info(std::string_view name)
|
||||
asst::TaskData::taskptr_t asst::TaskData::generate_task_info(std::string_view name, const json::value& task_json,
|
||||
taskptr_t default_ptr, std::string_view task_prefix)
|
||||
{
|
||||
auto raw = get_raw(name);
|
||||
if (!raw) [[unlikely]] {
|
||||
Log.error("Task", name, "not found");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto json_it = m_json_all_tasks_info.find(name);
|
||||
const json::value& json = json_it == m_json_all_tasks_info.cend() ? json::value {} : json_it->second;
|
||||
if (raw->type == TaskDerivedType::Raw && json_it == m_json_all_tasks_info.cend()) [[unlikely]] {
|
||||
Log.error("Task", name, "of type Raw has no json");
|
||||
return nullptr;
|
||||
}
|
||||
if (raw->type == TaskDerivedType::Raw && !raw->base.empty()) [[unlikely]] {
|
||||
Log.error("Task", name, "of type Raw has base", raw->base);
|
||||
return nullptr;
|
||||
}
|
||||
if (raw->type != TaskDerivedType::Raw && raw->base.empty()) [[unlikely]] {
|
||||
Log.error("Task", name, "of type", enum_to_string(raw->type), "has no base");
|
||||
return nullptr;
|
||||
}
|
||||
if ((raw->type == TaskDerivedType::Implicit || raw->type == TaskDerivedType::Template) && raw->prefix.empty())
|
||||
[[unlikely]] {
|
||||
Log.error("Task", name, "of type", enum_to_string(raw->type), "has no prefix");
|
||||
return nullptr;
|
||||
}
|
||||
if ((raw->type == TaskDerivedType::Raw || raw->type == TaskDerivedType::BaseTask) && !raw->prefix.empty())
|
||||
[[unlikely]] {
|
||||
Log.error("Task", name, "of type", enum_to_string(raw->type), "has prefix", raw->prefix);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
TaskConstPtr base = default_task_info_ptr;
|
||||
if (!raw->base.empty()) {
|
||||
base = get(raw->base);
|
||||
if (!base) [[unlikely]] {
|
||||
Log.error("Base task", raw->base, "of task", name, "not found");
|
||||
return nullptr;
|
||||
}
|
||||
if (default_ptr == nullptr) {
|
||||
default_ptr = default_task_info_ptr;
|
||||
task_prefix = "";
|
||||
}
|
||||
|
||||
// 获取 algorithm 并按照 algorithm 生成 TaskInfo
|
||||
AlgorithmType algorithm;
|
||||
utils::get_value_or(name, json, "algorithm", algorithm, base->algorithm);
|
||||
TaskPtr task = nullptr;
|
||||
utils::get_value_or(name, task_json, "algorithm", algorithm, default_ptr->algorithm);
|
||||
|
||||
taskptr_t task_ptr = nullptr;
|
||||
switch (algorithm) {
|
||||
case AlgorithmType::MatchTemplate:
|
||||
task = generate_match_task_info(name, json, std::dynamic_pointer_cast<const MatchTaskInfo>(base), raw->type);
|
||||
task_ptr = generate_match_task_info(name, task_json, std::dynamic_pointer_cast<MatchTaskInfo>(default_ptr));
|
||||
break;
|
||||
case AlgorithmType::OcrDetect:
|
||||
task = generate_ocr_task_info(name, json, std::dynamic_pointer_cast<const OcrTaskInfo>(base));
|
||||
task_ptr = generate_ocr_task_info(name, task_json, std::dynamic_pointer_cast<OcrTaskInfo>(default_ptr));
|
||||
break;
|
||||
case AlgorithmType::Hash:
|
||||
task = generate_hash_task_info(name, json, std::dynamic_pointer_cast<const HashTaskInfo>(base));
|
||||
task_ptr = generate_hash_task_info(name, task_json, std::dynamic_pointer_cast<HashTaskInfo>(default_ptr));
|
||||
break;
|
||||
case AlgorithmType::JustReturn:
|
||||
task = std::make_shared<TaskInfo>();
|
||||
task_ptr = std::make_shared<TaskInfo>();
|
||||
break;
|
||||
default:
|
||||
Log.error("Unknown algorithm in task", name);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (task == nullptr) {
|
||||
if (task_ptr == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const auto& prefix = raw->prefix;
|
||||
|
||||
#define ASST_TASKDATA_GET_VALUE_OR(key, value) utils::get_value_or(name, json, key, task->value, base->value)
|
||||
#define ASST_TASKDATA_GET_VALUE_OR_LAZY(key, value) \
|
||||
utils::get_value_or(name, json, key, task->value, [&]() { return append_prefix(base->value, prefix); })
|
||||
|
||||
ASST_TASKDATA_GET_VALUE_OR("action", action);
|
||||
ASST_TASKDATA_GET_VALUE_OR("cache", cache);
|
||||
ASST_TASKDATA_GET_VALUE_OR("maxTimes", max_times);
|
||||
ASST_TASKDATA_GET_VALUE_OR("preDelay", pre_delay);
|
||||
ASST_TASKDATA_GET_VALUE_OR("postDelay", post_delay);
|
||||
ASST_TASKDATA_GET_VALUE_OR("roi", roi);
|
||||
ASST_TASKDATA_GET_VALUE_OR("subErrorIgnored", sub_error_ignored);
|
||||
ASST_TASKDATA_GET_VALUE_OR("rectMove", rect_move);
|
||||
ASST_TASKDATA_GET_VALUE_OR("specificRect", specific_rect);
|
||||
ASST_TASKDATA_GET_VALUE_OR("specialParams", special_params);
|
||||
ASST_TASKDATA_GET_VALUE_OR_LAZY("next", next);
|
||||
ASST_TASKDATA_GET_VALUE_OR_LAZY("sub", sub);
|
||||
ASST_TASKDATA_GET_VALUE_OR_LAZY("exceededNext", exceeded_next);
|
||||
ASST_TASKDATA_GET_VALUE_OR_LAZY("onErrorNext", on_error_next);
|
||||
ASST_TASKDATA_GET_VALUE_OR_LAZY("reduceOtherTimes", reduce_other_times);
|
||||
|
||||
#undef ASST_TASKDATA_GET_VALUE_OR
|
||||
#undef ASST_TASKDATA_GET_VALUE_OR_LAZY
|
||||
|
||||
#ifdef ASST_DEBUG
|
||||
// Debug 模式下检查 roi 是否超出边界
|
||||
if (auto [x, y, w, h] = task->roi; x + w > WindowWidthDefault || y + h > WindowHeightDefault) {
|
||||
Log.warn(name, "roi is out of bounds");
|
||||
// 不管什么algorithm,都有基础成员(next, roi, 等等)
|
||||
if (!append_base_task_info(task_ptr, name, task_json, default_ptr, task_prefix)) {
|
||||
return nullptr;
|
||||
}
|
||||
#endif
|
||||
task->algorithm = algorithm;
|
||||
task->name = name;
|
||||
|
||||
return task;
|
||||
task_ptr->algorithm = algorithm;
|
||||
task_ptr->name = name;
|
||||
return task_ptr;
|
||||
}
|
||||
|
||||
asst::TaskPtr asst::TaskData::generate_match_task_info(std::string_view name, const json::value& task_json,
|
||||
MatchTaskConstPtr default_ptr, TaskDerivedType derived_type)
|
||||
asst::TaskData::taskptr_t asst::TaskData::generate_match_task_info(std::string_view name, const json::value& task_json,
|
||||
std::shared_ptr<MatchTaskInfo> default_ptr)
|
||||
{
|
||||
if (default_ptr == nullptr) {
|
||||
default_ptr = default_match_task_info_ptr;
|
||||
}
|
||||
auto match_task_info_ptr = std::make_shared<MatchTaskInfo>();
|
||||
if (!utils::get_value_or(name, task_json, "template", match_task_info_ptr->templ_names, [&]() {
|
||||
return derived_type == TaskDerivedType::Implicit // 隐式 Template Task 时继承,其它时默认值使用任务名
|
||||
? default_ptr->templ_names
|
||||
: std::vector { std::string(name) + ".png" };
|
||||
})) {
|
||||
if (!utils::get_value_or(name, task_json, "template", match_task_info_ptr->templ_names,
|
||||
[&]() { return std::vector { std::string(name) + ".png" }; })) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -460,7 +381,7 @@ asst::TaskPtr asst::TaskData::generate_match_task_info(std::string_view name, co
|
||||
threshold_opt->as_double());
|
||||
}
|
||||
else if (threshold_opt->is_array()) {
|
||||
ranges::copy(threshold_opt->as_array() | views::transform(&json::value::as_double),
|
||||
ranges::copy(threshold_opt->as_array() | views::transform(&ranges::range_value_t<json::array>::as_double),
|
||||
std::back_inserter(match_task_info_ptr->templ_thresholds));
|
||||
}
|
||||
else {
|
||||
@@ -482,8 +403,8 @@ asst::TaskPtr asst::TaskData::generate_match_task_info(std::string_view name, co
|
||||
return match_task_info_ptr;
|
||||
}
|
||||
|
||||
asst::TaskPtr asst::TaskData::generate_ocr_task_info(std::string_view name, const json::value& task_json,
|
||||
OcrTaskConstPtr default_ptr)
|
||||
asst::TaskData::taskptr_t asst::TaskData::generate_ocr_task_info(std::string_view name, const json::value& task_json,
|
||||
std::shared_ptr<OcrTaskInfo> default_ptr)
|
||||
{
|
||||
if (default_ptr == nullptr) {
|
||||
default_ptr = default_ocr_task_info_ptr;
|
||||
@@ -506,8 +427,8 @@ asst::TaskPtr asst::TaskData::generate_ocr_task_info(std::string_view name, cons
|
||||
return ocr_task_info_ptr;
|
||||
}
|
||||
|
||||
asst::TaskPtr asst::TaskData::generate_hash_task_info(std::string_view name, const json::value& task_json,
|
||||
HashTaskConstPtr default_ptr)
|
||||
asst::TaskData::taskptr_t asst::TaskData::generate_hash_task_info(std::string_view name, const json::value& task_json,
|
||||
std::shared_ptr<HashTaskInfo> default_ptr)
|
||||
{
|
||||
if (default_ptr == nullptr) {
|
||||
default_ptr = default_hash_task_info_ptr;
|
||||
@@ -527,149 +448,44 @@ asst::TaskPtr asst::TaskData::generate_hash_task_info(std::string_view name, con
|
||||
return hash_task_info_ptr;
|
||||
}
|
||||
|
||||
asst::ResultOrError<asst::TaskData::RawCompileResult> asst::TaskData::compile_raw_tasklist(
|
||||
const TaskList& raw_tasks, std::string_view self_name, std::function<TaskDerivedConstPtr(std::string_view)> get_raw,
|
||||
bool allow_duplicate)
|
||||
bool asst::TaskData::append_base_task_info(taskptr_t task_info_ptr, std::string_view name, const json::value& task_json,
|
||||
taskptr_t default_ptr, std::string_view task_prefix)
|
||||
{
|
||||
RawCompileResult ret { .task_changed = false, .symbols = {} };
|
||||
std::unordered_set<std::string_view> tasks_set; // 记录任务列表中已有的任务(内容元素与 new_tasks 基本一致)
|
||||
|
||||
for (std::string_view task_expr : raw_tasks) {
|
||||
if (task_expr.empty() || (!allow_duplicate && tasks_set.contains(task_expr))) {
|
||||
ret.task_changed = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
TaskDataSymbolStream symbol_stream;
|
||||
if (auto opt = symbol_stream.parse(task_expr); opt) [[likely]] {
|
||||
ret.task_changed = ret.task_changed || opt.value();
|
||||
}
|
||||
else {
|
||||
return { std::nullopt, std::move(opt.error()) };
|
||||
}
|
||||
|
||||
auto opt = symbol_stream.decode(
|
||||
[&](const TaskDataSymbol& symbol, const TaskDataSymbol& prefix) -> TaskDataSymbol::SymbolsOrError {
|
||||
return TaskDataSymbol::append_prefix(
|
||||
symbol, prefix, self_name, get_raw,
|
||||
[&](const TaskList& raw_or_empty) -> TaskDataSymbol::SymbolsOrError {
|
||||
if (auto opt = compile_raw_tasklist(raw_or_empty, self_name, get_raw, allow_duplicate)) {
|
||||
return opt.value().symbols;
|
||||
}
|
||||
return { std::nullopt, "" };
|
||||
});
|
||||
},
|
||||
self_name);
|
||||
|
||||
// 记号流处理
|
||||
if (!opt) {
|
||||
return { std::nullopt, std::move(opt.error()) };
|
||||
}
|
||||
|
||||
for (const auto& task : *opt) {
|
||||
if (task.name().empty() || (!allow_duplicate && tasks_set.contains(task.name()))) {
|
||||
ret.task_changed = true;
|
||||
continue;
|
||||
}
|
||||
tasks_set.emplace(ret.symbols.emplace_back(task).name());
|
||||
}
|
||||
|
||||
tasks_set.emplace(task_name_view(task_expr));
|
||||
if (default_ptr == nullptr) {
|
||||
default_ptr = default_task_info_ptr;
|
||||
}
|
||||
|
||||
return ret;
|
||||
utils::get_value_or(name, task_json, "action", task_info_ptr->action, default_ptr->action);
|
||||
utils::get_value_or(name, task_json, "cache", task_info_ptr->cache, default_ptr->cache);
|
||||
utils::get_value_or(name, task_json, "maxTimes", task_info_ptr->max_times, default_ptr->max_times);
|
||||
utils::get_value_or(name, task_json, "exceededNext", task_info_ptr->exceeded_next,
|
||||
[&]() { return append_prefix(default_ptr->exceeded_next, task_prefix); });
|
||||
utils::get_value_or(name, task_json, "onErrorNext", task_info_ptr->on_error_next,
|
||||
[&]() { return append_prefix(default_ptr->on_error_next, task_prefix); });
|
||||
utils::get_value_or(name, task_json, "preDelay", task_info_ptr->pre_delay, default_ptr->pre_delay);
|
||||
utils::get_value_or(name, task_json, "postDelay", task_info_ptr->post_delay, default_ptr->post_delay);
|
||||
utils::get_value_or(name, task_json, "reduceOtherTimes", task_info_ptr->reduce_other_times,
|
||||
[&]() { return append_prefix(default_ptr->reduce_other_times, task_prefix); });
|
||||
utils::get_value_or(name, task_json, "roi", task_info_ptr->roi, default_ptr->roi);
|
||||
utils::get_value_or(name, task_json, "sub", task_info_ptr->sub,
|
||||
[&]() { return append_prefix(default_ptr->sub, task_prefix); });
|
||||
utils::get_value_or(name, task_json, "subErrorIgnored", task_info_ptr->sub_error_ignored,
|
||||
default_ptr->sub_error_ignored);
|
||||
utils::get_value_or(name, task_json, "next", task_info_ptr->next,
|
||||
[&]() { return append_prefix(default_ptr->next, task_prefix); });
|
||||
utils::get_value_or(name, task_json, "rectMove", task_info_ptr->rect_move, default_ptr->rect_move);
|
||||
utils::get_value_or(name, task_json, "specificRect", task_info_ptr->specific_rect, default_ptr->specific_rect);
|
||||
utils::get_value_or(name, task_json, "specialParams", task_info_ptr->special_params, default_ptr->special_params);
|
||||
#ifdef ASST_DEBUG
|
||||
// Debug 模式下检查 roi 是否超出边界
|
||||
if (auto [x, y, w, h] = task_info_ptr->roi; x + w > WindowWidthDefault || y + h > WindowHeightDefault) {
|
||||
Log.warn(name, "roi is out of bounds");
|
||||
}
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
asst::ResultOrError<asst::TaskData::CompileResult> asst::TaskData::compile_tasklist(const TaskList& raw_tasks,
|
||||
std::string_view self_name,
|
||||
bool allow_duplicate)
|
||||
{
|
||||
CompileResult ret { .task_changed = false, .tasks = {} };
|
||||
std::vector<TaskDataSymbol> new_symbols;
|
||||
if (auto opt = compile_raw_tasklist(
|
||||
raw_tasks, self_name, [&](std::string_view name) { return get_raw(name); }, allow_duplicate);
|
||||
!opt) {
|
||||
return { std::nullopt, std::move(opt.error()) };
|
||||
}
|
||||
else {
|
||||
new_symbols = std::move(opt.value().symbols);
|
||||
ret.task_changed = opt.value().task_changed;
|
||||
}
|
||||
std::unordered_set<std::string_view> tasks_set;
|
||||
for (const auto& task : new_symbols) {
|
||||
std::string_view task_name;
|
||||
if (task == TaskDataSymbol::SharpSelf) {
|
||||
task_name = self_name;
|
||||
ret.task_changed = true;
|
||||
}
|
||||
else if (task.is_name()) {
|
||||
task_name = task.name();
|
||||
}
|
||||
else {
|
||||
ret.task_changed = true;
|
||||
continue;
|
||||
}
|
||||
if (task_name.empty()) {
|
||||
Log.error("Empty task name in", self_name);
|
||||
ret.task_changed = true;
|
||||
continue;
|
||||
}
|
||||
if (!allow_duplicate && tasks_set.contains(task_name)) {
|
||||
ret.task_changed = true;
|
||||
continue;
|
||||
}
|
||||
ret.tasks.emplace_back(task_name_view(task_name));
|
||||
tasks_set.emplace(task_name);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::string asst::TaskData::append_prefix(std::string_view task_name, std::string_view task_prefix)
|
||||
{
|
||||
if (task_prefix.ends_with('@')) [[unlikely]] {
|
||||
task_prefix.remove_suffix(1);
|
||||
}
|
||||
if (task_prefix.empty()) [[unlikely]] {
|
||||
return std::string(task_name);
|
||||
}
|
||||
if (task_name.empty()) {
|
||||
return std::string(task_prefix);
|
||||
}
|
||||
if (task_name.starts_with('#')) {
|
||||
return std::string(task_prefix) + std::string(task_name);
|
||||
}
|
||||
return std::string(task_prefix) + '@' + std::string(task_name);
|
||||
}
|
||||
|
||||
asst::TaskList asst::TaskData::append_prefix(const TaskList& base_task_list, std::string_view task_prefix)
|
||||
{
|
||||
if (task_prefix.ends_with('@')) [[unlikely]] {
|
||||
task_prefix.remove_suffix(1);
|
||||
}
|
||||
if (task_prefix.empty()) {
|
||||
return base_task_list;
|
||||
}
|
||||
TaskList task_list = {};
|
||||
for (const std::string& base : base_task_list) {
|
||||
std::string_view base_view = base;
|
||||
size_t l = 0;
|
||||
bool has_same_prefix = false;
|
||||
// base 任务里已经存在相同前缀,则不加前缀
|
||||
// https://github.com/MaaAssistantArknights/MaaAssistantArknights/pull/2116#issuecomment-1270115238
|
||||
for (size_t r = base_view.find('@'); r != std::string_view::npos; r = base_view.find('@', l)) {
|
||||
if (task_prefix == base_view.substr(l, r - l)) [[unlikely]] {
|
||||
has_same_prefix = true;
|
||||
break;
|
||||
}
|
||||
l = r + 1;
|
||||
}
|
||||
task_list.emplace_back(has_same_prefix ? base : append_prefix(base_view, task_prefix));
|
||||
}
|
||||
return task_list;
|
||||
}
|
||||
|
||||
asst::MatchTaskConstPtr asst::TaskData::_default_match_task_info()
|
||||
std::shared_ptr<asst::MatchTaskInfo> asst::TaskData::_default_match_task_info()
|
||||
{
|
||||
auto match_task_info_ptr = std::make_shared<MatchTaskInfo>();
|
||||
match_task_info_ptr->templ_names = { "__INVALID__" };
|
||||
@@ -678,7 +494,7 @@ asst::MatchTaskConstPtr asst::TaskData::_default_match_task_info()
|
||||
return match_task_info_ptr;
|
||||
}
|
||||
|
||||
asst::OcrTaskConstPtr asst::TaskData::_default_ocr_task_info()
|
||||
std::shared_ptr<asst::OcrTaskInfo> asst::TaskData::_default_ocr_task_info()
|
||||
{
|
||||
auto ocr_task_info_ptr = std::make_shared<OcrTaskInfo>();
|
||||
ocr_task_info_ptr->full_match = false;
|
||||
@@ -689,7 +505,7 @@ asst::OcrTaskConstPtr asst::TaskData::_default_ocr_task_info()
|
||||
return ocr_task_info_ptr;
|
||||
}
|
||||
|
||||
asst::HashTaskConstPtr asst::TaskData::_default_hash_task_info()
|
||||
std::shared_ptr<asst::HashTaskInfo> asst::TaskData::_default_hash_task_info()
|
||||
{
|
||||
auto hash_task_info_ptr = std::make_shared<HashTaskInfo>();
|
||||
hash_task_info_ptr->dist_threshold = 0;
|
||||
@@ -698,7 +514,7 @@ asst::HashTaskConstPtr asst::TaskData::_default_hash_task_info()
|
||||
return hash_task_info_ptr;
|
||||
}
|
||||
|
||||
asst::TaskConstPtr asst::TaskData::_default_task_info()
|
||||
asst::TaskData::taskptr_t asst::TaskData::_default_task_info()
|
||||
{
|
||||
auto task_info_ptr = std::make_shared<TaskInfo>();
|
||||
task_info_ptr->algorithm = AlgorithmType::MatchTemplate;
|
||||
@@ -715,6 +531,396 @@ asst::TaskConstPtr asst::TaskData::_default_task_info()
|
||||
return task_info_ptr;
|
||||
}
|
||||
|
||||
// new_tasks 是目的任务列表
|
||||
// raw_tasks 是源任务表达式列表
|
||||
// task_name 是任务名
|
||||
// task_changed 记录 raw_tasks 在转换为 new_tasks 时是否发生变化
|
||||
// allow_duplicate 是否允许重复
|
||||
bool asst::TaskData::compile_tasklist(tasklist_t& new_tasks, const tasklist_t& raw_tasks, std::string_view task_name,
|
||||
bool& task_changed, bool allow_duplicate)
|
||||
{
|
||||
std::unordered_set<std::string_view> tasks_set; // 记录任务列表中已有的任务(内容元素与 new_tasks 基本一致)
|
||||
|
||||
using symbl_t = size_t;
|
||||
[[maybe_unused]] constexpr symbl_t symbl_end = 0;
|
||||
[[maybe_unused]] constexpr symbl_t symbl_lambda_task_sep = 1;
|
||||
[[maybe_unused]] constexpr symbl_t symbl_lparen = 2;
|
||||
[[maybe_unused]] constexpr symbl_t symbl_rparen = 3;
|
||||
[[maybe_unused]] constexpr symbl_t symbl_lbrace = 4;
|
||||
[[maybe_unused]] constexpr symbl_t symbl_rbrace = 5;
|
||||
[[maybe_unused]] constexpr symbl_t symbl_at = 6;
|
||||
[[maybe_unused]] constexpr symbl_t symbl_sharp = 7;
|
||||
[[maybe_unused]] constexpr symbl_t symbl_mul = 8;
|
||||
[[maybe_unused]] constexpr symbl_t symbl_add = 9;
|
||||
[[maybe_unused]] constexpr symbl_t symbl_sub = 10;
|
||||
|
||||
[[maybe_unused]] constexpr symbl_t symbl_name_start = 11;
|
||||
[[maybe_unused]] constexpr symbl_t symbl_name_sub = 11;
|
||||
[[maybe_unused]] constexpr symbl_t symbl_name_next = 12;
|
||||
[[maybe_unused]] constexpr symbl_t symbl_name_on_error_next = 13;
|
||||
[[maybe_unused]] constexpr symbl_t symbl_name_exceeded_next = 14;
|
||||
[[maybe_unused]] constexpr symbl_t symbl_name_reduce_other_times = 15;
|
||||
[[maybe_unused]] constexpr symbl_t symbl_name_self = 16;
|
||||
[[maybe_unused]] constexpr symbl_t symbl_name_back = 17;
|
||||
[[maybe_unused]] constexpr symbl_t symbl_name_none = 18;
|
||||
|
||||
static const std::vector<std::string> symbl_table = {
|
||||
"__END__", // 0
|
||||
",", // 1
|
||||
"(", // 2
|
||||
")", // 3
|
||||
"{", // 4
|
||||
"}", // 5
|
||||
"@", // 6
|
||||
"#", // 7
|
||||
"*", // 8
|
||||
"+", // 9
|
||||
"^", // 10
|
||||
"sub", // 11
|
||||
"next", // 12
|
||||
"on_error_next", // 13
|
||||
"exceeded_next", // 14
|
||||
"reduce_other_times", // 15
|
||||
"self", // 16
|
||||
"back", // 17
|
||||
"none", // 18
|
||||
};
|
||||
|
||||
auto is_symbl_name = [&](symbl_t x) { return x >= symbl_name_start; };
|
||||
auto is_symbl_subtask_type = [&](symbl_t x) {
|
||||
switch (x) {
|
||||
case symbl_name_sub:
|
||||
case symbl_name_next:
|
||||
case symbl_name_on_error_next:
|
||||
case symbl_name_exceeded_next:
|
||||
case symbl_name_reduce_other_times:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
};
|
||||
[[maybe_unused]] auto is_symbl_sharp_type = [&](symbl_t x) {
|
||||
switch (x) {
|
||||
case symbl_name_self:
|
||||
case symbl_name_back:
|
||||
case symbl_name_none:
|
||||
return true;
|
||||
default:
|
||||
return is_symbl_subtask_type(x);
|
||||
}
|
||||
};
|
||||
|
||||
// perform_op 的结果不保证符合参数 multi 的要求
|
||||
auto perform_op = [&](std::string_view task_expr, symbl_t op, tasklistptr_t x,
|
||||
tasklistptr_t y) -> std::optional<asst::TaskData::tasklistptr_t> {
|
||||
auto ret = std::make_shared<tasklist_t>();
|
||||
|
||||
switch (op) {
|
||||
case symbl_add:
|
||||
ranges::copy(*x, std::back_inserter(*ret));
|
||||
ranges::copy(*y, std::back_inserter(*ret));
|
||||
break;
|
||||
case symbl_sub:
|
||||
for (std::string_view sx : *x) {
|
||||
bool flag = true;
|
||||
for (std::string_view sy : *y) {
|
||||
if (sx == sy) {
|
||||
flag = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (flag) ret->emplace_back(sx);
|
||||
}
|
||||
break;
|
||||
case symbl_mul: {
|
||||
if (y->size() != 1) {
|
||||
Log.error("There is more than one y:", *y, "while perform op", symbl_table[op], "in", task_expr,
|
||||
"of task:", task_name);
|
||||
return std::nullopt;
|
||||
}
|
||||
int times = 0;
|
||||
try {
|
||||
times = std::stoi(y->front());
|
||||
}
|
||||
catch (...) {
|
||||
Log.error("y:", y->front(), "is not number while perform op", symbl_table[op], "in", task_expr,
|
||||
"of task:", task_name);
|
||||
return std::nullopt;
|
||||
}
|
||||
for (int i = 0; i < times; ++i) {
|
||||
ranges::copy(*x, std::back_inserter(*ret));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case symbl_at: {
|
||||
if (y->empty()) { // A@#none = A
|
||||
ranges::copy(*x, std::back_inserter(*ret));
|
||||
}
|
||||
else if (x->empty()) { // #none@A = A
|
||||
ranges::copy(*y, std::back_inserter(*ret));
|
||||
}
|
||||
else { // (A+B)@(C+D) = A@C + A@D + B@C + B@D
|
||||
ranges::for_each(
|
||||
*x, [&](std::string_view s) { ranges::copy(append_prefix(*y, s), std::back_inserter(*ret)); });
|
||||
}
|
||||
break;
|
||||
}
|
||||
case symbl_sharp: {
|
||||
if (y->empty()) {
|
||||
Log.error("There is no sharp_type while perform op", symbl_table[op], "in", task_expr,
|
||||
"of task:", task_name);
|
||||
return std::nullopt;
|
||||
}
|
||||
for (std::string_view type : *y) {
|
||||
if (!x || x->empty()) { // unary
|
||||
x = std::make_shared<tasklist_t>(tasklist_t { "" });
|
||||
}
|
||||
for (const auto& task : *x) {
|
||||
if (type == "self") {
|
||||
ret->emplace_back(task_name);
|
||||
continue;
|
||||
}
|
||||
if (type == "none") {
|
||||
continue;
|
||||
}
|
||||
if (type == "back") {
|
||||
// "A#back" === "A", "B@A#back" === "B@A", "#back" === null
|
||||
if (!task.empty()) ret->emplace_back(task);
|
||||
continue;
|
||||
}
|
||||
taskptr_t other_task_info_ptr = task.empty() ? default_task_info_ptr : get_raw(task);
|
||||
|
||||
#define ASST_TASKDATA_PERFORM_OP_IF_BRANCH(t, m) \
|
||||
else if (type == #t) \
|
||||
{ \
|
||||
bool task_changed = false; \
|
||||
if (!compile_tasklist(*ret, other_task_info_ptr->t, task_name, task_changed, m)) { \
|
||||
Log.error("Failed to compile task", task + "->" #t, "while perform op", symbl_table[op], "in", task_expr, \
|
||||
"of task:", task_name); \
|
||||
return std::nullopt; \
|
||||
} \
|
||||
}
|
||||
if (other_task_info_ptr == nullptr) [[unlikely]] {
|
||||
Log.error("Task", task, "not found while perform op", symbl_table[op], "in", task_expr,
|
||||
"of task:", task_name);
|
||||
return std::nullopt;
|
||||
}
|
||||
ASST_TASKDATA_PERFORM_OP_IF_BRANCH(next, false)
|
||||
ASST_TASKDATA_PERFORM_OP_IF_BRANCH(sub, true)
|
||||
ASST_TASKDATA_PERFORM_OP_IF_BRANCH(on_error_next, false)
|
||||
ASST_TASKDATA_PERFORM_OP_IF_BRANCH(exceeded_next, false)
|
||||
ASST_TASKDATA_PERFORM_OP_IF_BRANCH(reduce_other_times, true)
|
||||
else [[unlikely]]
|
||||
{
|
||||
Log.error("Unknown symbol", type, "while perform op", symbl_table[op], "in", task_expr,
|
||||
"of task:", task_name);
|
||||
return std::nullopt;
|
||||
}
|
||||
#undef ASST_TASKDATA_PERFORM_OP_IF_BRANCH
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
Log.error("Unknown op", symbl_table[op], "in", task_expr, "of task:", task_name);
|
||||
return std::nullopt;
|
||||
}
|
||||
return ret;
|
||||
};
|
||||
|
||||
for (std::string_view task_expr : raw_tasks) {
|
||||
if (task_expr.empty() || (!allow_duplicate && tasks_set.contains(task_expr))) {
|
||||
task_changed = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
std::vector<std::string> symbols_table = symbl_table;
|
||||
|
||||
std::unordered_map<std::string, symbl_t> symbols_id = {};
|
||||
for (symbl_t i = 0; i != symbols_table.size(); ++i) {
|
||||
symbols_id.emplace(symbols_table[i], i);
|
||||
}
|
||||
|
||||
std::vector<symbl_t> symbol_stream;
|
||||
auto emplace_symbol_if_not_empty = [&](const auto l, const auto r) {
|
||||
if (l < r) {
|
||||
auto symbol = std::string(l, r);
|
||||
if (!symbols_id.contains(symbol)) {
|
||||
symbl_t id = symbols_table.size();
|
||||
symbols_id.emplace(symbol, id);
|
||||
symbols_table.emplace_back(symbol);
|
||||
symbol_stream.emplace_back(id);
|
||||
}
|
||||
else {
|
||||
symbol_stream.emplace_back(symbols_id[symbol]);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 记号流分析
|
||||
auto y_begin = task_expr.begin();
|
||||
for (auto p = task_expr.begin(); p != task_expr.end(); ++p) {
|
||||
switch (*p) {
|
||||
case ' ':
|
||||
case '\t':
|
||||
case '\r':
|
||||
case '\n':
|
||||
emplace_symbol_if_not_empty(y_begin, p);
|
||||
y_begin = p + 1;
|
||||
break;
|
||||
case ',':
|
||||
case '(':
|
||||
case ')':
|
||||
case '{':
|
||||
case '}':
|
||||
case '#':
|
||||
case '*':
|
||||
case '+':
|
||||
case '^':
|
||||
task_changed = true;
|
||||
[[fallthrough]];
|
||||
case '@':
|
||||
emplace_symbol_if_not_empty(y_begin, p);
|
||||
y_begin = p + 1;
|
||||
symbol_stream.emplace_back(symbols_id[std::string(1, *p)]);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
emplace_symbol_if_not_empty(y_begin, task_expr.end());
|
||||
symbol_stream.emplace_back(symbl_end); // 结束符
|
||||
// Log.debug(symbol_stream | views::transform([&](symbl_t id) { return symbols_table[id]; }));
|
||||
// Log.debug(symbol_stream);
|
||||
|
||||
/*
|
||||
$name = 至少一位的任务名
|
||||
$numbers = 至少一位的数字
|
||||
|
||||
$subtask_type = sub
|
||||
| next
|
||||
| on_error_next
|
||||
| exceeded_next
|
||||
| reduce_other_times
|
||||
|
||||
$sharp_type = $subtask_type
|
||||
| self
|
||||
| back
|
||||
| none
|
||||
|
||||
$subtask = $subtask_type ( $tasks )
|
||||
|
||||
$lambda_task = { $subtask , ... } // 暂未实现
|
||||
| { $tasks } // 暂未实现;不写 subtask_type 默认为 sub
|
||||
|
||||
$parens = # $sharp_type
|
||||
| $name
|
||||
| $name $lambda_task // 暂未实现
|
||||
| ( $tasks )
|
||||
|
||||
$top_tasks = $top_tasks @ $parens
|
||||
| $top_tasks # $sharp_type
|
||||
| $parens
|
||||
|
||||
$mul_tasks = $top_tasks * $numbers
|
||||
| $top_tasks
|
||||
|
||||
$tasks = $mul_tasks + $tasks
|
||||
| $mul_tasks ^ $tasks // 删除 $mul_tasks 中所有在 $tasks 中的任务
|
||||
| $mul_tasks
|
||||
*/
|
||||
// 记号流处理
|
||||
auto cur = symbol_stream.cbegin();
|
||||
using decode_func_t = std::function<std::optional<tasklistptr_t>()>;
|
||||
decode_func_t decode_name, decode_tasks, decode_multasks, decode_vtasks, decode_parens;
|
||||
decode_name = [&]() -> std::optional<tasklistptr_t> {
|
||||
if (is_symbl_name(*cur)) {
|
||||
return std::make_shared<tasklist_t>(tasklist_t { symbols_table[*(cur++)] });
|
||||
}
|
||||
return std::nullopt;
|
||||
};
|
||||
decode_tasks = [&]() -> std::optional<tasklistptr_t> {
|
||||
auto l = decode_multasks();
|
||||
if (!l) return std::nullopt;
|
||||
while (*cur == symbl_add || *cur == symbl_sub) {
|
||||
symbl_t op = *(cur++);
|
||||
auto r = decode_multasks();
|
||||
if (!r) return std::nullopt;
|
||||
l = perform_op(task_expr, op, *l, *r);
|
||||
if (!l) return std::nullopt;
|
||||
}
|
||||
return l;
|
||||
};
|
||||
decode_multasks = [&]() -> std::optional<tasklistptr_t> {
|
||||
auto l = decode_vtasks();
|
||||
if (!l) return std::nullopt;
|
||||
while (*cur == symbl_mul) {
|
||||
symbl_t op = *(cur++);
|
||||
auto r = decode_name();
|
||||
if (!r) return std::nullopt;
|
||||
l = perform_op(task_expr, op, *l, *r);
|
||||
if (!l) return std::nullopt;
|
||||
}
|
||||
return l;
|
||||
};
|
||||
decode_vtasks = [&]() -> std::optional<tasklistptr_t> {
|
||||
auto l = decode_parens();
|
||||
if (!l) return std::nullopt;
|
||||
while (*cur == symbl_at || *cur == symbl_sharp) {
|
||||
symbl_t op = *(cur++);
|
||||
auto r = decode_parens();
|
||||
if (!r) return std::nullopt;
|
||||
l = perform_op(task_expr, op, *l, *r);
|
||||
if (!l) return std::nullopt;
|
||||
}
|
||||
return l;
|
||||
};
|
||||
decode_parens = [&]() -> std::optional<tasklistptr_t> {
|
||||
if (*cur == symbl_lparen) {
|
||||
++cur;
|
||||
auto l = decode_tasks();
|
||||
if (!l) return std::nullopt;
|
||||
if (*cur != symbl_rparen) [[unlikely]] {
|
||||
Log.error("Invalid symbol", *cur, "at", cur - symbol_stream.cbegin(), "in", symbol_stream,
|
||||
"(should be ')')");
|
||||
return std::nullopt;
|
||||
}
|
||||
++cur;
|
||||
return l;
|
||||
}
|
||||
if (*cur == symbl_sharp) {
|
||||
++cur;
|
||||
auto r = decode_name();
|
||||
if (!r) return std::nullopt;
|
||||
return perform_op(task_expr, symbl_sharp, nullptr, *r);
|
||||
}
|
||||
if (is_symbl_name(*cur)) {
|
||||
return decode_name();
|
||||
}
|
||||
Log.error("Invalid symbol", *cur, "at", cur - symbol_stream.cbegin(), "in", symbol_stream);
|
||||
return std::nullopt;
|
||||
};
|
||||
auto opt = decode_tasks();
|
||||
if (!opt || (*cur != symbl_end && cur != symbol_stream.cend())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const auto& task : **opt) {
|
||||
if (task.empty() || (!allow_duplicate && tasks_set.contains(task))) {
|
||||
task_changed = true;
|
||||
continue;
|
||||
}
|
||||
new_tasks.emplace_back(task);
|
||||
tasks_set.emplace(task_name_view(task));
|
||||
}
|
||||
|
||||
tasks_set.emplace(task_name_view(task_expr));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef ASST_DEBUG
|
||||
// 为了解决类似 beddc7c828126c678391e0b4da288db6d2c2d58a 导致的问题,加载的时候做一个语法检查
|
||||
// 主要是处理是否包含未知键值的问题
|
||||
|
||||
@@ -2,83 +2,156 @@
|
||||
|
||||
#include "AbstractConfigWithTempl.h"
|
||||
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
|
||||
#include "Common/AsstTypes.h"
|
||||
#include "TaskData/TaskDataSymbol.h"
|
||||
|
||||
namespace asst
|
||||
{
|
||||
struct TaskInfo;
|
||||
|
||||
class TaskData final : public SingletonHolder<TaskData>, public AbstractConfigWithTempl
|
||||
{
|
||||
private:
|
||||
static MatchTaskConstPtr _default_match_task_info();
|
||||
static OcrTaskConstPtr _default_ocr_task_info();
|
||||
static HashTaskConstPtr _default_hash_task_info();
|
||||
static TaskConstPtr _default_task_info();
|
||||
using tasklist_t = std::vector<std::string>;
|
||||
using tasklistptr_t = std::shared_ptr<tasklist_t>;
|
||||
using taskptr_t = std::shared_ptr<TaskInfo>;
|
||||
|
||||
std::shared_ptr<MatchTaskInfo> _default_match_task_info();
|
||||
std::shared_ptr<OcrTaskInfo> _default_ocr_task_info();
|
||||
std::shared_ptr<HashTaskInfo> _default_hash_task_info();
|
||||
taskptr_t _default_task_info();
|
||||
|
||||
// 从模板任务生成
|
||||
static inline const MatchTaskConstPtr default_match_task_info_ptr = _default_match_task_info();
|
||||
static inline const OcrTaskConstPtr default_ocr_task_info_ptr = _default_ocr_task_info();
|
||||
static inline const HashTaskConstPtr default_hash_task_info_ptr = _default_hash_task_info();
|
||||
static inline const TaskConstPtr default_task_info_ptr = _default_task_info();
|
||||
const std::shared_ptr<MatchTaskInfo> default_match_task_info_ptr = _default_match_task_info();
|
||||
const std::shared_ptr<OcrTaskInfo> default_ocr_task_info_ptr = _default_ocr_task_info();
|
||||
const std::shared_ptr<HashTaskInfo> default_hash_task_info_ptr = _default_hash_task_info();
|
||||
const taskptr_t default_task_info_ptr = _default_task_info();
|
||||
|
||||
static std::string append_prefix(std::string_view task_name, std::string_view task_prefix);
|
||||
static TaskList append_prefix(const TaskList& base_task_list, std::string_view task_prefix);
|
||||
// 这是下面几个函数的封装
|
||||
taskptr_t generate_task_info(std::string_view name, const json::value&, taskptr_t default_ptr,
|
||||
std::string_view task_prefix = "");
|
||||
|
||||
taskptr_t generate_match_task_info(std::string_view name, const json::value&,
|
||||
std::shared_ptr<MatchTaskInfo> default_ptr);
|
||||
taskptr_t generate_ocr_task_info(std::string_view name, const json::value&,
|
||||
std::shared_ptr<OcrTaskInfo> default_ptr);
|
||||
taskptr_t generate_hash_task_info(std::string_view name, const json::value&,
|
||||
std::shared_ptr<HashTaskInfo> default_ptr);
|
||||
// TaskInfo 的基础成员
|
||||
bool append_base_task_info(taskptr_t task_info_ptr, std::string_view name, const json::value& task_json,
|
||||
taskptr_t default_ptr, std::string_view task_prefix = "");
|
||||
static std::string append_prefix(std::string_view task_name, std::string_view task_prefix)
|
||||
{
|
||||
if (task_prefix.ends_with('@')) [[unlikely]] {
|
||||
task_prefix.remove_suffix(1);
|
||||
}
|
||||
if (task_prefix.empty()) [[unlikely]] {
|
||||
return std::string(task_name);
|
||||
}
|
||||
if (task_name.empty()) {
|
||||
return std::string(task_prefix);
|
||||
}
|
||||
if (task_name.starts_with('#')) {
|
||||
return std::string(task_prefix) + std::string(task_name);
|
||||
}
|
||||
return std::string(task_prefix) + '@' + std::string(task_name);
|
||||
}
|
||||
static tasklist_t append_prefix(const tasklist_t& base_task_list, std::string_view task_prefix)
|
||||
{
|
||||
if (task_prefix.ends_with('@')) [[unlikely]] {
|
||||
task_prefix.remove_suffix(1);
|
||||
}
|
||||
if (task_prefix.empty()) {
|
||||
return base_task_list;
|
||||
}
|
||||
tasklist_t task_list = {};
|
||||
for (const std::string& base : base_task_list) {
|
||||
std::string_view base_view = base;
|
||||
size_t l = 0;
|
||||
bool has_same_prefix = false;
|
||||
// base 任务里已经存在相同前缀,则不加前缀
|
||||
// https://github.com/MaaAssistantArknights/MaaAssistantArknights/pull/2116#issuecomment-1270115238
|
||||
for (size_t r = base_view.find('@'); r != std::string_view::npos; r = base_view.find('@', l)) {
|
||||
if (task_prefix == base_view.substr(l, r - l)) [[unlikely]] {
|
||||
has_same_prefix = true;
|
||||
break;
|
||||
}
|
||||
l = r + 1;
|
||||
}
|
||||
task_list.emplace_back(has_same_prefix ? base : append_prefix(base_view, task_prefix));
|
||||
}
|
||||
return task_list;
|
||||
};
|
||||
|
||||
// 运行时动态生成任务
|
||||
static taskptr_t _generate_task_info(const taskptr_t& base_ptr)
|
||||
{
|
||||
if (!base_ptr) [[unlikely]] {
|
||||
return nullptr;
|
||||
}
|
||||
switch (base_ptr->algorithm) {
|
||||
case AlgorithmType::MatchTemplate:
|
||||
return std::make_shared<MatchTaskInfo>(*std::dynamic_pointer_cast<MatchTaskInfo>(base_ptr));
|
||||
case AlgorithmType::OcrDetect:
|
||||
return std::make_shared<OcrTaskInfo>(*std::dynamic_pointer_cast<OcrTaskInfo>(base_ptr));
|
||||
case AlgorithmType::Hash:
|
||||
return std::make_shared<HashTaskInfo>(*std::dynamic_pointer_cast<HashTaskInfo>(base_ptr));
|
||||
default:
|
||||
return std::make_shared<TaskInfo>(*base_ptr);
|
||||
}
|
||||
}
|
||||
static taskptr_t _generate_task_info(const taskptr_t& base_ptr, std::string_view task_prefix)
|
||||
{
|
||||
taskptr_t task_info_ptr = _generate_task_info(base_ptr);
|
||||
if (!task_prefix.empty()) {
|
||||
task_info_ptr->name = append_prefix(base_ptr->name, task_prefix);
|
||||
task_info_ptr->sub = append_prefix(base_ptr->sub, task_prefix);
|
||||
task_info_ptr->next = append_prefix(base_ptr->next, task_prefix);
|
||||
task_info_ptr->exceeded_next = append_prefix(base_ptr->exceeded_next, task_prefix);
|
||||
task_info_ptr->on_error_next = append_prefix(base_ptr->on_error_next, task_prefix);
|
||||
task_info_ptr->reduce_other_times = append_prefix(base_ptr->reduce_other_times, task_prefix);
|
||||
}
|
||||
return task_info_ptr;
|
||||
}
|
||||
template <ranges::forward_range ListType>
|
||||
requires(!std::same_as<ranges::range_value_t<ListType>, std::string> &&
|
||||
requires { std::declval<ranges::range_value_t<ListType>>().as_string(); })
|
||||
static TaskList to_string_list(const ListType& other_string_list)
|
||||
static tasklist_t to_string_list(const ListType& other_string_list)
|
||||
{
|
||||
TaskList task_list = {};
|
||||
tasklist_t task_list = {};
|
||||
task_list.reserve(other_string_list.size());
|
||||
ranges::copy(other_string_list | views::transform(&ranges::range_value_t<ListType>::as_string),
|
||||
std::back_inserter(task_list));
|
||||
return task_list;
|
||||
}
|
||||
|
||||
static inline std::unordered_set<std::string> m_task_names {};
|
||||
static const std::string& task_name_view(std::string_view name) { return *m_task_names.emplace(name).first; }
|
||||
|
||||
struct RawCompileResult
|
||||
{
|
||||
bool task_changed;
|
||||
TaskDataSymbol::Symbols symbols;
|
||||
};
|
||||
static ResultOrError<RawCompileResult> compile_raw_tasklist(
|
||||
const TaskList& raw_tasks, std::string_view self_name,
|
||||
std::function<TaskDerivedConstPtr(std::string_view)> get_raw, bool allow_duplicate);
|
||||
|
||||
private:
|
||||
TaskPtr generate_task_info(std::string_view name);
|
||||
TaskPtr generate_match_task_info(std::string_view name, const json::value&, MatchTaskConstPtr default_ptr,
|
||||
TaskDerivedType derived_type);
|
||||
TaskPtr generate_ocr_task_info(std::string_view name, const json::value&, OcrTaskConstPtr default_ptr);
|
||||
TaskPtr generate_hash_task_info(std::string_view name, const json::value&, HashTaskConstPtr default_ptr);
|
||||
decltype(auto) insert_or_assign_raw_task(std::string_view task_name, TaskDerivedPtr task_info_ptr)
|
||||
std::string_view task_name_view(std::string_view task_name) { return *m_task_names.emplace(task_name).first; }
|
||||
decltype(auto) insert_or_assign_raw_task(std::string_view task_name, taskptr_t task_info_ptr)
|
||||
{
|
||||
return m_raw_all_tasks_info.insert_or_assign(task_name_view(task_name), task_info_ptr);
|
||||
}
|
||||
decltype(auto) insert_or_assign_task(std::string_view task_name, TaskPtr task_info_ptr)
|
||||
decltype(auto) insert_or_assign_task(std::string_view task_name, taskptr_t task_info_ptr)
|
||||
{
|
||||
return m_all_tasks_info.insert_or_assign(task_name_view(task_name), task_info_ptr);
|
||||
}
|
||||
struct CompileResult
|
||||
{
|
||||
bool task_changed;
|
||||
TaskList tasks;
|
||||
};
|
||||
ResultOrError<CompileResult> compile_tasklist(const TaskList& raw_tasks, std::string_view self_name,
|
||||
bool allow_duplicate);
|
||||
bool generate_raw_task_info(std::string_view name, std::string_view prefix, std::string_view base_name,
|
||||
const json::value& task_json, TaskDerivedType type);
|
||||
bool generate_raw_task_and_base(std::string_view name, bool must_true);
|
||||
bool compile_tasklist(tasklist_t& new_tasks, const tasklist_t& raw_tasks, std::string_view name,
|
||||
bool& task_changed, bool multi);
|
||||
bool generate_task_and_its_base(std::string_view name, bool must_true);
|
||||
#ifdef ASST_DEBUG
|
||||
bool syntax_check(std::string_view task_name, const json::value& task_json);
|
||||
#endif
|
||||
TaskDerivedConstPtr get_raw(std::string_view name);
|
||||
taskptr_t get_raw(std::string_view name);
|
||||
template <typename TargetTaskInfoType>
|
||||
requires(std::derived_from<TargetTaskInfoType, TaskInfo> &&
|
||||
!std::same_as<TargetTaskInfoType, TaskInfo>) // Parameter must be a TaskInfo
|
||||
std::shared_ptr<TargetTaskInfoType> get_raw(std::string_view name)
|
||||
{
|
||||
return std::dynamic_pointer_cast<TargetTaskInfoType>(get_raw(name));
|
||||
}
|
||||
|
||||
public:
|
||||
virtual ~TaskData() override = default;
|
||||
@@ -87,15 +160,21 @@ namespace asst
|
||||
void set_task_base(const std::string_view task_name, std::string base_task_name);
|
||||
bool lazy_parse(const json::value& json);
|
||||
|
||||
TaskPtr get(std::string_view name);
|
||||
taskptr_t get(std::string_view name);
|
||||
template <typename TargetTaskInfoType>
|
||||
requires(std::derived_from<TargetTaskInfoType, TaskInfo> &&
|
||||
!std::same_as<TargetTaskInfoType, TaskInfo>) // Parameter must be a TaskInfo
|
||||
std::shared_ptr<TargetTaskInfoType> get(std::string_view name)
|
||||
{
|
||||
// TODO: should be const
|
||||
return std::dynamic_pointer_cast<TargetTaskInfoType>(get(name));
|
||||
}
|
||||
std::optional<json::object> get_json(std::string_view name) const
|
||||
{
|
||||
if (m_json_all_tasks_info.find(name) != m_json_all_tasks_info.cend())
|
||||
return m_json_all_tasks_info.at(name);
|
||||
else
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
protected:
|
||||
enum TaskStatus
|
||||
@@ -108,11 +187,12 @@ namespace asst
|
||||
|
||||
virtual bool parse(const json::value& json) override;
|
||||
|
||||
std::unordered_set<std::string> m_task_names;
|
||||
std::unordered_set<std::string> m_templ_required;
|
||||
std::unordered_map<std::string_view, TaskStatus> m_task_status;
|
||||
std::unordered_map<std::string_view, json::object> m_json_all_tasks_info; // 原始的 json 信息
|
||||
std::unordered_map<std::string_view, TaskDerivedPtr> m_raw_all_tasks_info; // 未展开虚任务的任务信息
|
||||
std::unordered_map<std::string_view, TaskPtr> m_all_tasks_info; // 已展开虚任务的任务信息
|
||||
std::unordered_map<std::string_view, json::object> m_json_all_tasks_info; // 原始的 json 信息
|
||||
std::unordered_map<std::string_view, taskptr_t> m_raw_all_tasks_info; // 未展开虚任务的任务信息
|
||||
std::unordered_map<std::string_view, taskptr_t> m_all_tasks_info; // 已展开虚任务的任务信息
|
||||
};
|
||||
|
||||
inline static auto& Task = TaskData::get_instance();
|
||||
|
||||
@@ -1,55 +0,0 @@
|
||||
#include "TaskDataSymbol.h"
|
||||
|
||||
asst::TaskDataSymbol::SymbolsOrError asst::TaskDataSymbol::append_prefix(
|
||||
const TaskDataSymbol& symbol, const TaskDataSymbol& prefix, std::string_view self_name,
|
||||
std::function<TaskDerivedConstPtr(std::string_view)> get_raw,
|
||||
std::function<SymbolsOrError(const TaskList&)> compile_tasklist)
|
||||
{
|
||||
// 注意:A@#self 是 A 而不是 A@self_name, #self@A 是 self_name@A 而不是 A
|
||||
std::string_view prefix_name;
|
||||
if (prefix == SharpSelf) {
|
||||
prefix_name = self_name;
|
||||
}
|
||||
else if (prefix.is_name()) {
|
||||
prefix_name = prefix.name();
|
||||
}
|
||||
else [[unlikely]] {
|
||||
return { std::nullopt, "prefix " + prefix.name() + " is not name or self" };
|
||||
}
|
||||
if (prefix_name.empty()) {
|
||||
return std::vector { symbol };
|
||||
}
|
||||
if (symbol == SharpNone) {
|
||||
return std::vector { symbol };
|
||||
}
|
||||
if (symbol == SharpSelf) {
|
||||
return std::vector { symbol };
|
||||
}
|
||||
if (symbol.is_name()) {
|
||||
if (symbol.name().empty()) {
|
||||
return { std::nullopt, "name is empty" };
|
||||
}
|
||||
return std::vector { TaskDataSymbol(std::string(prefix_name) + '@' + symbol.name()) };
|
||||
}
|
||||
if (symbol == SharpBack) {
|
||||
return std::vector { TaskDataSymbol(prefix_name) };
|
||||
}
|
||||
auto other_task_info_ptr = get_raw(prefix_name);
|
||||
#define ASST_TASKDATA_PERFORM_OP_IF_BRANCH(t, s, m) \
|
||||
if (symbol == s) { \
|
||||
if (auto opt = compile_tasklist(other_task_info_ptr->t)) { \
|
||||
return opt.value(); \
|
||||
} \
|
||||
return { std::nullopt, "failed to compile tasklist" }; \
|
||||
}
|
||||
if (other_task_info_ptr == nullptr) [[unlikely]] {
|
||||
return { std::nullopt, "task not found" };
|
||||
}
|
||||
ASST_TASKDATA_PERFORM_OP_IF_BRANCH(next, SharpNext, false)
|
||||
ASST_TASKDATA_PERFORM_OP_IF_BRANCH(sub, SharpSub, true)
|
||||
ASST_TASKDATA_PERFORM_OP_IF_BRANCH(on_error_next, SharpOnErrorNext, false)
|
||||
ASST_TASKDATA_PERFORM_OP_IF_BRANCH(exceeded_next, SharpExceededNext, false)
|
||||
ASST_TASKDATA_PERFORM_OP_IF_BRANCH(reduce_other_times, SharpReduceOtherTimes, true)
|
||||
#undef ASST_TASKDATA_PERFORM_OP_IF_BRANCH
|
||||
return { std::nullopt, "unknown symbol" };
|
||||
}
|
||||
@@ -1,135 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <unordered_map>
|
||||
#include <variant>
|
||||
|
||||
#include "Common/AsstTypes.h"
|
||||
#include "TaskDataTypes.h"
|
||||
#include "Utils/Logger.hpp"
|
||||
|
||||
namespace asst
|
||||
{
|
||||
class TaskDataSymbol final
|
||||
{
|
||||
public:
|
||||
using Symbols = std::vector<TaskDataSymbol>;
|
||||
using SymbolsOrError = ResultOrError<Symbols>;
|
||||
|
||||
enum Type
|
||||
{
|
||||
End,
|
||||
LambdaTaskSep,
|
||||
LParen,
|
||||
RParen,
|
||||
LBrace,
|
||||
RBrace,
|
||||
At,
|
||||
Sharp,
|
||||
Mul,
|
||||
Add,
|
||||
Sub,
|
||||
SharpSub,
|
||||
SharpNext,
|
||||
SharpOnErrorNext,
|
||||
SharpExceededNext,
|
||||
SharpReduceOtherTimes,
|
||||
SharpSelf,
|
||||
SharpBack,
|
||||
SharpNone,
|
||||
Name,
|
||||
};
|
||||
|
||||
static const inline std::unordered_map<std::string_view, Type> symbol_repr_to_type = {
|
||||
{ "__END__", End },
|
||||
{ ",", LambdaTaskSep },
|
||||
{ "(", LParen },
|
||||
{ ")", RParen },
|
||||
{ "{", LBrace },
|
||||
{ "}", RBrace },
|
||||
{ "@", At },
|
||||
{ "#", Sharp },
|
||||
{ "*", Mul },
|
||||
{ "+", Add },
|
||||
{ "^", Sub },
|
||||
{ "sub", SharpSub },
|
||||
{ "next", SharpNext },
|
||||
{ "on_error_next", SharpOnErrorNext },
|
||||
{ "exceeded_next", SharpExceededNext },
|
||||
{ "reduce_other_times", SharpReduceOtherTimes },
|
||||
{ "self", SharpSelf },
|
||||
{ "back", SharpBack },
|
||||
{ "none", SharpNone },
|
||||
{ "__name__", Name },
|
||||
};
|
||||
static const inline std::unordered_map<Type, std::string> symbol_type_to_repr = {
|
||||
{ End, "__END__" },
|
||||
{ LambdaTaskSep, "," },
|
||||
{ LParen, "(" },
|
||||
{ RParen, ")" },
|
||||
{ LBrace, "{" },
|
||||
{ RBrace, "}" },
|
||||
{ At, "@" },
|
||||
{ Sharp, "#" },
|
||||
{ Mul, "*" },
|
||||
{ Add, "+" },
|
||||
{ Sub, "^" },
|
||||
{ SharpSub, "sub" },
|
||||
{ SharpNext, "next" },
|
||||
{ SharpOnErrorNext, "on_error_next" },
|
||||
{ SharpExceededNext, "exceeded_next" },
|
||||
{ SharpReduceOtherTimes, "reduce_other_times" },
|
||||
{ SharpSelf, "self" },
|
||||
{ SharpBack, "back" },
|
||||
{ SharpNone, "none" },
|
||||
{ Name, "__name__" },
|
||||
};
|
||||
|
||||
Type m_symbol;
|
||||
std::string m_name;
|
||||
|
||||
public:
|
||||
bool operator==(Type other) const noexcept { return m_symbol == other; }
|
||||
bool operator==(std::string_view other) const noexcept { return m_symbol == Name && m_name == other; }
|
||||
bool operator==(const TaskDataSymbol& other) const noexcept
|
||||
{
|
||||
return m_symbol == other.m_symbol && (m_symbol != Name || m_name == other.m_name);
|
||||
}
|
||||
TaskDataSymbol(Type symbol) : m_symbol(symbol) {}
|
||||
template <typename StringT>
|
||||
requires(std::constructible_from<std::string, StringT>)
|
||||
TaskDataSymbol(StringT&& name) : m_symbol(Name), m_name(std::forward<StringT>(name))
|
||||
{}
|
||||
|
||||
static bool is_sharp_type(Type type) noexcept
|
||||
{
|
||||
static std::unordered_set<Type> sharp_types = {
|
||||
SharpSub, SharpNext, SharpOnErrorNext, SharpExceededNext, SharpReduceOtherTimes,
|
||||
SharpSelf, SharpBack, SharpNone,
|
||||
};
|
||||
return sharp_types.contains(type);
|
||||
}
|
||||
static const std::string& repr(Type type)
|
||||
{
|
||||
const auto pos = symbol_type_to_repr.find(type);
|
||||
return pos == symbol_type_to_repr.end() ? symbol_type_to_repr.at(Name) : pos->second;
|
||||
}
|
||||
static Type type(std::string_view repr)
|
||||
{
|
||||
const auto pos = symbol_repr_to_type.find(repr);
|
||||
return pos == symbol_repr_to_type.end() ? Name : pos->second;
|
||||
}
|
||||
|
||||
static SymbolsOrError append_prefix(const TaskDataSymbol& symbol, const TaskDataSymbol& prefix,
|
||||
std::string_view self_name,
|
||||
std::function<TaskDerivedConstPtr(std::string_view)> get_raw,
|
||||
std::function<SymbolsOrError(const TaskList&)> compile_tasklist);
|
||||
|
||||
bool is_name() const noexcept { return m_symbol == Name; }
|
||||
bool is_sharp_type() const noexcept { return is_sharp_type(m_symbol); }
|
||||
Type type() const noexcept { return m_symbol; }
|
||||
// const std::string& repr() const& noexcept { return repr(m_symbol); }
|
||||
const std::string& name() const& noexcept { return is_name() ? m_name : repr(m_symbol); }
|
||||
};
|
||||
} // namespace asst
|
||||
@@ -1,265 +0,0 @@
|
||||
#include "TaskDataSymbolStream.h"
|
||||
|
||||
asst::ResultOrError<bool> asst::TaskDataSymbolStream::parse(std::string_view task_expr)
|
||||
{
|
||||
bool task_changed = false;
|
||||
auto emplace_symbol_if_not_empty = [&](const auto l, const auto r) {
|
||||
if (l < r) {
|
||||
auto symbol = std::string_view(l, r);
|
||||
auto symbol_type = Symbol::type(symbol);
|
||||
if (Symbol::is_sharp_type(symbol_type)) {
|
||||
if (!m_symbolstream.empty() && m_symbolstream.back() == Symbol::Sharp) {
|
||||
m_symbolstream.pop_back();
|
||||
if (!m_symbolstream.empty() &&
|
||||
(m_symbolstream.back().is_name() || m_symbolstream.back().is_sharp_type() ||
|
||||
m_symbolstream.back() == Symbol::RParen)) {
|
||||
m_symbolstream.emplace_back(Symbol::At);
|
||||
}
|
||||
m_symbolstream.emplace_back(symbol_type);
|
||||
}
|
||||
else {
|
||||
m_symbolstream.emplace_back(symbol);
|
||||
}
|
||||
}
|
||||
else if (symbol_type != Symbol::Name) {
|
||||
m_symbolstream.emplace_back(symbol_type);
|
||||
}
|
||||
else {
|
||||
m_symbolstream.emplace_back(symbol);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 记号流分析
|
||||
auto y_begin = task_expr.cbegin();
|
||||
for (auto p = task_expr.cbegin(); p != task_expr.cend(); ++p) {
|
||||
switch (*p) {
|
||||
case ' ':
|
||||
case '\t':
|
||||
case '\r':
|
||||
case '\n':
|
||||
task_changed = true;
|
||||
emplace_symbol_if_not_empty(y_begin, p);
|
||||
y_begin = p + 1;
|
||||
break;
|
||||
case ',':
|
||||
case '(':
|
||||
case ')':
|
||||
case '{':
|
||||
case '}':
|
||||
case '#':
|
||||
case '*':
|
||||
case '+':
|
||||
case '^':
|
||||
task_changed = true;
|
||||
[[fallthrough]];
|
||||
case '@': {
|
||||
emplace_symbol_if_not_empty(y_begin, p);
|
||||
y_begin = p + 1;
|
||||
auto symbol = TaskDataSymbol::type(std::string_view { std::addressof(*p), 1 });
|
||||
if (symbol == TaskDataSymbol::Name) [[unlikely]] {
|
||||
// should not happen
|
||||
return { std::nullopt, std::string("Error when decode symbol ") + *p + " at " +
|
||||
std::to_string(p - task_expr.cbegin()) + " in " += task_expr };
|
||||
}
|
||||
m_symbolstream.emplace_back(symbol);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
emplace_symbol_if_not_empty(y_begin, task_expr.cend());
|
||||
m_symbolstream.emplace_back(TaskDataSymbol::End); // 结束符
|
||||
|
||||
return task_changed;
|
||||
}
|
||||
|
||||
asst::TaskDataSymbolStream::SymbolsOrError asst::TaskDataSymbolStream::decode(AppendPrefixFunc append_prefix,
|
||||
std::string_view self_name) const
|
||||
{
|
||||
/*
|
||||
$name = 至少一位的任务名
|
||||
$numbers = 至少一位的数字
|
||||
|
||||
$subtask_type = # sub
|
||||
| # next
|
||||
| # on_error_next
|
||||
| # exceeded_next
|
||||
| # reduce_other_times
|
||||
|
||||
$sharp_type = $subtask_type
|
||||
| # self
|
||||
| # back
|
||||
| # none
|
||||
|
||||
$subtask = $subtask_type ( $tasks )
|
||||
|
||||
$lambda_task = { $subtask , ... } // 暂未实现
|
||||
| { $tasks } // 暂未实现;不写 subtask_type 默认为 sub
|
||||
|
||||
$parens = $sharp_type
|
||||
| $name
|
||||
| $name $lambda_task // 暂未实现
|
||||
| ( $tasks )
|
||||
|
||||
$top_tasks = $top_tasks @ $parens
|
||||
| $parens
|
||||
|
||||
$mul_tasks = $top_tasks * $numbers
|
||||
| $top_tasks
|
||||
|
||||
$tasks = $mul_tasks + $tasks
|
||||
| $mul_tasks ^ $tasks // 删除 $mul_tasks 中所有在 $tasks 中的任务
|
||||
| $mul_tasks
|
||||
*/
|
||||
using decode_func_t = std::function<SymbolsOrError()>;
|
||||
|
||||
auto cur = m_symbolstream.cbegin();
|
||||
decode_func_t decode_name_or_vtask, decode_tasks, decode_multasks, decode_vtasks, decode_parens;
|
||||
decode_name_or_vtask = [&]() -> SymbolsOrError {
|
||||
if (cur->is_name() || cur->is_sharp_type()) {
|
||||
return Symbols { *(cur++) };
|
||||
}
|
||||
return { std::nullopt, "expect name or sharp type, got " + cur->name() };
|
||||
};
|
||||
decode_tasks = [&]() -> SymbolsOrError {
|
||||
Symbols x = {}, y = {};
|
||||
if (auto opt = decode_multasks(); !opt) {
|
||||
return opt;
|
||||
}
|
||||
else {
|
||||
x = *opt;
|
||||
}
|
||||
while (*cur == Symbol::Add || *cur == Symbol::Sub) {
|
||||
Symbol op = *(cur++);
|
||||
if (auto opt = decode_multasks(); !opt) {
|
||||
return opt;
|
||||
}
|
||||
else {
|
||||
y = *opt;
|
||||
}
|
||||
if (op == Symbol::Add) {
|
||||
// x = x + y
|
||||
ranges::move(y, std::back_inserter(x));
|
||||
}
|
||||
else {
|
||||
// x = x - y
|
||||
// WARNING:
|
||||
// self_name + #self ^ #self = #none
|
||||
// self_name + #self ^ self_name = #self
|
||||
if (ranges::any_of(y, [&](const auto& sy) { return sy == Symbol::SharpSelf; })) {
|
||||
std::erase(x, self_name);
|
||||
}
|
||||
std::erase_if(x, [&](const auto& sx) {
|
||||
return ranges::any_of(y, [&](const auto& sy) { return sx == sy; });
|
||||
});
|
||||
}
|
||||
}
|
||||
return x;
|
||||
};
|
||||
decode_multasks = [&]() -> SymbolsOrError {
|
||||
Symbols x = {}, y = {};
|
||||
if (auto opt = decode_vtasks(); !opt) {
|
||||
return opt;
|
||||
}
|
||||
else {
|
||||
x = *opt;
|
||||
}
|
||||
while (*cur == Symbol::Mul) {
|
||||
Symbol op = *(cur++);
|
||||
if (auto opt = decode_name_or_vtask(); !opt) {
|
||||
return opt;
|
||||
}
|
||||
else {
|
||||
y = *opt;
|
||||
}
|
||||
if (y.size() != 1) {
|
||||
return { std::nullopt, "too many y" };
|
||||
}
|
||||
int times = 0;
|
||||
if (const auto s = y.front(); !s.is_name() || !utils::chars_to_number<int, true>(s.name(), times)) {
|
||||
return { std::nullopt, "y is not number" };
|
||||
}
|
||||
if (times < 0) {
|
||||
return { std::nullopt, "y is negative" };
|
||||
}
|
||||
if (times == 0) {
|
||||
x = {};
|
||||
Log.warn("y:", times, "is zero");
|
||||
continue;
|
||||
}
|
||||
if (times == 1) {
|
||||
continue;
|
||||
}
|
||||
Symbols x_copy = x;
|
||||
for (int i = 0; i < times - 1; ++i) {
|
||||
ranges::copy(x_copy, std::back_inserter(x));
|
||||
}
|
||||
}
|
||||
return x;
|
||||
};
|
||||
decode_vtasks = [&]() -> SymbolsOrError {
|
||||
Symbols x = {}, y = {};
|
||||
if (auto opt = decode_parens(); !opt) {
|
||||
return opt;
|
||||
}
|
||||
else {
|
||||
x = *opt;
|
||||
}
|
||||
while (*cur == Symbol::At) {
|
||||
Symbol op = *(cur++);
|
||||
if (auto opt = decode_parens(); !opt) {
|
||||
return opt;
|
||||
}
|
||||
else {
|
||||
y = *opt;
|
||||
}
|
||||
// (A+B)@(C+D) = A@C + A@D + B@C + B@D
|
||||
Symbols x_copy = x;
|
||||
x.clear();
|
||||
for (const auto& sx : x_copy) {
|
||||
for (const auto& sy : y) {
|
||||
auto opt = append_prefix(sy, sx);
|
||||
if (!opt) {
|
||||
return { std::nullopt,
|
||||
"decode_vtasks: failed while " + sx.name() + " @ " + sy.name() + ", " + opt.error() };
|
||||
}
|
||||
ranges::copy(*opt, std::back_inserter(x));
|
||||
}
|
||||
}
|
||||
}
|
||||
return x;
|
||||
};
|
||||
decode_parens = [&]() -> SymbolsOrError {
|
||||
if (*cur == Symbol::LParen) {
|
||||
++cur;
|
||||
Symbols x = {};
|
||||
if (auto opt = decode_tasks(); !opt) {
|
||||
return opt;
|
||||
}
|
||||
else {
|
||||
x = *opt;
|
||||
}
|
||||
if (*cur != Symbol::RParen) [[unlikely]] {
|
||||
return { std::nullopt, "decode_parens: expected ')', got " + cur->name() };
|
||||
}
|
||||
++cur;
|
||||
return x;
|
||||
}
|
||||
if (cur->is_name() || cur->is_sharp_type()) {
|
||||
return decode_name_or_vtask();
|
||||
}
|
||||
return { std::nullopt, "decode_parens: unexpected symbol " + cur->name() };
|
||||
};
|
||||
|
||||
auto opt = decode_tasks();
|
||||
if (!opt) {
|
||||
return opt;
|
||||
}
|
||||
if (cur != m_symbolstream.cend() && *cur != TaskDataSymbol::End) {
|
||||
return { std::nullopt, "decode: did not reach end, got " + cur->name() };
|
||||
}
|
||||
|
||||
return opt;
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
#include "Config/TaskData.h"
|
||||
#include "TaskDataSymbol.h"
|
||||
#include "TaskDataTypes.h"
|
||||
|
||||
namespace asst
|
||||
{
|
||||
class TaskDataSymbolStream final
|
||||
{
|
||||
public:
|
||||
using Symbol = TaskDataSymbol;
|
||||
using Symbols = TaskDataSymbol::Symbols;
|
||||
using SymbolsOrError = TaskDataSymbol::SymbolsOrError;
|
||||
using AppendPrefixFunc = std::function<SymbolsOrError(const Symbol&, const Symbol&)>;
|
||||
|
||||
private:
|
||||
Symbols m_symbolstream;
|
||||
|
||||
public:
|
||||
ResultOrError<bool> parse(std::string_view task_expr);
|
||||
SymbolsOrError decode(AppendPrefixFunc append_prefix, std::string_view self_name) const;
|
||||
};
|
||||
} // namespace asst
|
||||
@@ -1,45 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <variant>
|
||||
|
||||
namespace asst
|
||||
{
|
||||
template <typename ResultT>
|
||||
class ResultOrError
|
||||
{
|
||||
using _variant_t = std::variant<ResultT, std::string>;
|
||||
bool m_success;
|
||||
_variant_t m_result_or_error;
|
||||
|
||||
public:
|
||||
template <typename... Args>
|
||||
requires std::constructible_from<ResultT, Args...>
|
||||
constexpr ResultOrError(Args&&... args)
|
||||
: m_success(true), m_result_or_error(std::in_place_index<0>, std::forward<Args>(args)...)
|
||||
{}
|
||||
|
||||
constexpr ResultOrError([[maybe_unused]] std::nullopt_t nullopt, std::string result)
|
||||
: m_success(false), m_result_or_error(std::in_place_index<1>, std::move(result))
|
||||
{}
|
||||
|
||||
constexpr operator bool() const noexcept { return m_success; }
|
||||
constexpr bool has_value() const noexcept { return m_success; }
|
||||
|
||||
constexpr ResultT& operator*() & { return std::get<0>(m_result_or_error); }
|
||||
constexpr const ResultT& operator*() const& { return std::get<0>(m_result_or_error); }
|
||||
constexpr ResultT&& operator*() && { return std::move(std::get<0>(m_result_or_error)); }
|
||||
constexpr const ResultT&& operator*() const&& { return std::move(std::get<0>(m_result_or_error)); }
|
||||
|
||||
constexpr ResultT& value() & { return std::get<0>(m_result_or_error); }
|
||||
constexpr const ResultT& value() const& { return std::get<0>(m_result_or_error); }
|
||||
constexpr ResultT&& value() && { return std::move(std::get<0>(m_result_or_error)); }
|
||||
constexpr const ResultT&& value() const&& { return std::move(std::get<0>(m_result_or_error)); }
|
||||
|
||||
constexpr std::string& error() & { return std::get<1>(m_result_or_error); }
|
||||
constexpr const std::string& error() const& { return std::get<1>(m_result_or_error); }
|
||||
constexpr std::string&& error() && { return std::move(std::get<1>(m_result_or_error)); }
|
||||
constexpr const std::string&& error() const&& { return std::move(std::get<1>(m_result_or_error)); }
|
||||
};
|
||||
} // namespace asst
|
||||
@@ -48,9 +48,6 @@
|
||||
<ClInclude Include="Config\Miscellaneous\OcrConfig.h" />
|
||||
<ClInclude Include="Config\Miscellaneous\SSSCopilotConfig.h" />
|
||||
<ClInclude Include="Config\OnnxSessions.h" />
|
||||
<ClInclude Include="Config\TaskData\TaskDataSymbol.h" />
|
||||
<ClInclude Include="Config\TaskData\TaskDataSymbolStream.h" />
|
||||
<ClInclude Include="Config\TaskData\TaskDataTypes.h" />
|
||||
<ClInclude Include="Controller\adb-lite\client.hpp" />
|
||||
<ClInclude Include="Controller\adb-lite\protocol.hpp" />
|
||||
<ClInclude Include="Controller\Controller.h" />
|
||||
@@ -232,8 +229,6 @@
|
||||
<ClCompile Include="Config\Miscellaneous\OcrConfig.cpp" />
|
||||
<ClCompile Include="Config\Miscellaneous\SSSCopilotConfig.cpp" />
|
||||
<ClCompile Include="Config\OnnxSessions.cpp" />
|
||||
<ClCompile Include="Config\TaskData\TaskDataSymbol.cpp" />
|
||||
<ClCompile Include="Config\TaskData\TaskDataSymbolStream.cpp" />
|
||||
<ClCompile Include="Controller\adb-lite\client.cpp" />
|
||||
<ClCompile Include="Controller\adb-lite\protocol.cpp" />
|
||||
<ClCompile Include="Controller\Controller.cpp" />
|
||||
|
||||
@@ -95,9 +95,6 @@
|
||||
<Filter Include="Source\Vision\Config">
|
||||
<UniqueIdentifier>{bf83678b-e9d7-4cf1-8616-aba2009072b6}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Source\Resource\TaskData">
|
||||
<UniqueIdentifier>{8d436f0b-8dad-42b2-9549-f9073b808104}</UniqueIdentifier>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="..\..\resource\config.json">
|
||||
@@ -684,15 +681,6 @@
|
||||
<ClInclude Include="Task\Roguelike\AbstractRoguelikeTaskPlugin.h">
|
||||
<Filter>Source\Task\Roguelike</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Config\TaskData\TaskDataSymbol.h">
|
||||
<Filter>Source\Resource\TaskData</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Config\TaskData\TaskDataSymbolStream.h">
|
||||
<Filter>Source\Resource\TaskData</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Config\TaskData\TaskDataTypes.h">
|
||||
<Filter>Source\Resource\TaskData</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="Vision\VisionHelper.cpp">
|
||||
@@ -1148,11 +1136,5 @@
|
||||
<ClCompile Include="Task\Roguelike\AbstractRoguelikeTaskPlugin.cpp">
|
||||
<Filter>Source\Task\Roguelike</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Config\TaskData\TaskDataSymbol.cpp">
|
||||
<Filter>Source\Resource\TaskData</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Config\TaskData\TaskDataSymbolStream.cpp">
|
||||
<Filter>Source\Resource\TaskData</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
Reference in New Issue
Block a user