MaaFw 4.5.1__py3-none-manylinux2014_x86_64.whl → 5.4.0b1__py3-none-manylinux2014_x86_64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- maa/agent/agent_server.py +221 -3
- maa/agent_client.py +170 -2
- maa/bin/libMaaAdbControlUnit.so +0 -0
- maa/bin/libMaaAgentClient.so +0 -0
- maa/bin/libMaaAgentServer.so +0 -0
- maa/bin/libMaaCustomControlUnit.so +0 -0
- maa/bin/libMaaFramework.so +0 -0
- maa/bin/libMaaToolkit.so +0 -0
- maa/bin/libMaaUtils.so +0 -0
- maa/bin/libc++.so.1 +0 -0
- maa/bin/libc++abi.so.1 +0 -0
- maa/bin/libfastdeploy_ppocr.so +0 -0
- maa/bin/libonnxruntime.so.1 +0 -0
- maa/bin/libopencv_world4.so.411 +0 -0
- maa/bin/libunwind.so.1 +0 -0
- maa/bin/plugins/libMaaPluginDemo.so +0 -0
- maa/buffer.py +168 -0
- maa/context.py +402 -10
- maa/controller.py +611 -36
- maa/custom_action.py +40 -0
- maa/custom_recognition.py +57 -5
- maa/define.py +432 -16
- maa/event_sink.py +103 -0
- maa/job.py +77 -1
- maa/library.py +118 -50
- maa/pipeline.py +509 -0
- maa/resource.py +430 -21
- maa/tasker.py +630 -75
- maa/toolkit.py +52 -91
- {maafw-4.5.1.dist-info → maafw-5.4.0b1.dist-info}/METADATA +87 -44
- maafw-5.4.0b1.dist-info/RECORD +35 -0
- {maafw-4.5.1.dist-info → maafw-5.4.0b1.dist-info}/WHEEL +1 -1
- maa/bin/libMaaDbgControlUnit.so +0 -0
- maa/notification_handler.py +0 -199
- maafw-4.5.1.dist-info/RECORD +0 -34
- {maafw-4.5.1.dist-info → maafw-5.4.0b1.dist-info}/licenses/LICENSE.md +0 -0
maa/tasker.py
CHANGED
|
@@ -1,19 +1,22 @@
|
|
|
1
1
|
import ctypes
|
|
2
|
+
import dataclasses
|
|
2
3
|
import json
|
|
3
4
|
from pathlib import Path
|
|
4
|
-
from typing import
|
|
5
|
+
from typing import Dict, Optional, Union
|
|
6
|
+
|
|
7
|
+
import numpy
|
|
5
8
|
|
|
6
9
|
from .define import *
|
|
7
10
|
from .library import Library
|
|
8
11
|
from .buffer import ImageListBuffer, RectBuffer, StringBuffer, ImageBuffer
|
|
9
12
|
from .job import Job, JobWithResult
|
|
10
|
-
from .
|
|
13
|
+
from .event_sink import EventSink, NotificationType
|
|
11
14
|
from .resource import Resource
|
|
12
15
|
from .controller import Controller
|
|
16
|
+
from .pipeline import JRecognitionParam, JActionParam, JRecognitionType, JActionType
|
|
13
17
|
|
|
14
18
|
|
|
15
19
|
class Tasker:
|
|
16
|
-
_notification_handler: Optional[NotificationHandler]
|
|
17
20
|
_handle: MaaTaskerHandle
|
|
18
21
|
_own: bool
|
|
19
22
|
|
|
@@ -21,19 +24,24 @@ class Tasker:
|
|
|
21
24
|
|
|
22
25
|
def __init__(
|
|
23
26
|
self,
|
|
24
|
-
notification_handler: Optional[NotificationHandler] = None,
|
|
25
27
|
handle: Optional[MaaTaskerHandle] = None,
|
|
26
28
|
):
|
|
29
|
+
"""创建实例 / Create instance
|
|
30
|
+
|
|
31
|
+
Args:
|
|
32
|
+
handle: 可选的外部句柄 / Optional external handle
|
|
33
|
+
|
|
34
|
+
Raises:
|
|
35
|
+
RuntimeError: 如果创建失败
|
|
36
|
+
"""
|
|
37
|
+
|
|
27
38
|
self._set_api_properties()
|
|
28
39
|
|
|
29
40
|
if handle:
|
|
30
41
|
self._handle = handle
|
|
31
42
|
self._own = False
|
|
32
43
|
else:
|
|
33
|
-
self.
|
|
34
|
-
self._handle = Library.framework().MaaTaskerCreate(
|
|
35
|
-
*NotificationHandler._gen_c_param(self._notification_handler)
|
|
36
|
-
)
|
|
44
|
+
self._handle = Library.framework().MaaTaskerCreate()
|
|
37
45
|
self._own = True
|
|
38
46
|
|
|
39
47
|
if not self._handle:
|
|
@@ -44,6 +52,15 @@ class Tasker:
|
|
|
44
52
|
Library.framework().MaaTaskerDestroy(self._handle)
|
|
45
53
|
|
|
46
54
|
def bind(self, resource: Resource, controller: Controller) -> bool:
|
|
55
|
+
"""关联资源和控制器 / Bind resource and controller
|
|
56
|
+
|
|
57
|
+
Args:
|
|
58
|
+
resource: 资源对象 / Resource object
|
|
59
|
+
controller: 控制器对象 / Controller object
|
|
60
|
+
|
|
61
|
+
Returns:
|
|
62
|
+
bool: 是否成功 / Whether successful
|
|
63
|
+
"""
|
|
47
64
|
# avoid gc
|
|
48
65
|
self._resource_holder = resource
|
|
49
66
|
self._controller_holder = controller
|
|
@@ -58,6 +75,14 @@ class Tasker:
|
|
|
58
75
|
|
|
59
76
|
@property
|
|
60
77
|
def resource(self) -> Resource:
|
|
78
|
+
"""获取关联的资源 / Get bound resource
|
|
79
|
+
|
|
80
|
+
Returns:
|
|
81
|
+
Resource: 资源对象 / Resource object
|
|
82
|
+
|
|
83
|
+
Raises:
|
|
84
|
+
RuntimeError: 如果获取失败
|
|
85
|
+
"""
|
|
61
86
|
resource_handle = Library.framework().MaaTaskerGetResource(self._handle)
|
|
62
87
|
if not resource_handle:
|
|
63
88
|
raise RuntimeError("Failed to get resource.")
|
|
@@ -66,6 +91,14 @@ class Tasker:
|
|
|
66
91
|
|
|
67
92
|
@property
|
|
68
93
|
def controller(self) -> Controller:
|
|
94
|
+
"""获取关联的控制器 / Get bound controller
|
|
95
|
+
|
|
96
|
+
Returns:
|
|
97
|
+
Controller: 控制器对象 / Controller object
|
|
98
|
+
|
|
99
|
+
Raises:
|
|
100
|
+
RuntimeError: 如果获取失败
|
|
101
|
+
"""
|
|
69
102
|
controller_handle = Library.framework().MaaTaskerGetController(self._handle)
|
|
70
103
|
if not controller_handle:
|
|
71
104
|
raise RuntimeError("Failed to get controller.")
|
|
@@ -74,28 +107,132 @@ class Tasker:
|
|
|
74
107
|
|
|
75
108
|
@property
|
|
76
109
|
def inited(self) -> bool:
|
|
110
|
+
"""判断是否正确初始化 / Check if initialized correctly
|
|
111
|
+
|
|
112
|
+
Returns:
|
|
113
|
+
bool: 是否已正确初始化 / Whether correctly initialized
|
|
114
|
+
"""
|
|
77
115
|
return bool(Library.framework().MaaTaskerInited(self._handle))
|
|
78
116
|
|
|
79
117
|
def post_task(self, entry: str, pipeline_override: Dict = {}) -> JobWithResult:
|
|
118
|
+
"""异步执行任务 / Asynchronously execute task
|
|
119
|
+
|
|
120
|
+
这是一个异步操作,会立即返回一个 Job 对象
|
|
121
|
+
This is an asynchronous operation that immediately returns a Job object
|
|
122
|
+
|
|
123
|
+
Args:
|
|
124
|
+
entry: 任务入口 / Task entry
|
|
125
|
+
pipeline_override: 用于覆盖的 json / JSON for overriding
|
|
126
|
+
|
|
127
|
+
Returns:
|
|
128
|
+
JobWithResult: 任务作业对象,可通过 status/wait 查询状态,通过 get() 获取结果 / Task job object, can query status via status/wait, get result via get()
|
|
129
|
+
"""
|
|
80
130
|
taskid = Library.framework().MaaTaskerPostTask(
|
|
81
131
|
self._handle,
|
|
82
132
|
*Tasker._gen_post_param(entry, pipeline_override),
|
|
83
133
|
)
|
|
84
134
|
return self._gen_task_job(taskid)
|
|
85
135
|
|
|
136
|
+
def post_recognition(
|
|
137
|
+
self,
|
|
138
|
+
reco_type: JRecognitionType,
|
|
139
|
+
reco_param: JRecognitionParam,
|
|
140
|
+
image: numpy.ndarray,
|
|
141
|
+
) -> JobWithResult:
|
|
142
|
+
"""异步执行识别 / Asynchronously execute recognition
|
|
143
|
+
|
|
144
|
+
Args:
|
|
145
|
+
reco_type: 识别类型 / Recognition type
|
|
146
|
+
reco_param: 识别参数 / Recognition parameters
|
|
147
|
+
image: 前序截图 / Previous screenshot
|
|
148
|
+
|
|
149
|
+
Returns:
|
|
150
|
+
JobWithResult: 任务作业对象 / Task job object
|
|
151
|
+
"""
|
|
152
|
+
img_buffer = ImageBuffer()
|
|
153
|
+
img_buffer.set(image)
|
|
154
|
+
reco_param_json = json.dumps(dataclasses.asdict(reco_param), ensure_ascii=False)
|
|
155
|
+
taskid = Library.framework().MaaTaskerPostRecognition(
|
|
156
|
+
self._handle,
|
|
157
|
+
reco_type.encode(),
|
|
158
|
+
reco_param_json.encode(),
|
|
159
|
+
img_buffer._handle,
|
|
160
|
+
)
|
|
161
|
+
return self._gen_task_job(taskid)
|
|
162
|
+
|
|
163
|
+
def post_action(
|
|
164
|
+
self,
|
|
165
|
+
action_type: JActionType,
|
|
166
|
+
action_param: JActionParam,
|
|
167
|
+
box: Rect = Rect(0, 0, 0, 0),
|
|
168
|
+
reco_detail: str = "",
|
|
169
|
+
) -> JobWithResult:
|
|
170
|
+
"""异步执行操作 / Asynchronously execute action
|
|
171
|
+
|
|
172
|
+
Args:
|
|
173
|
+
action_type: 操作类型 / Action type
|
|
174
|
+
action_param: 操作参数 / Action parameters
|
|
175
|
+
box: 前序识别位置 / Previous recognition position
|
|
176
|
+
reco_detail: 前序识别详情 / Previous recognition details
|
|
177
|
+
|
|
178
|
+
Returns:
|
|
179
|
+
JobWithResult: 任务作业对象 / Task job object
|
|
180
|
+
"""
|
|
181
|
+
rect_buffer = RectBuffer()
|
|
182
|
+
rect_buffer.set(box)
|
|
183
|
+
action_param_json = json.dumps(
|
|
184
|
+
dataclasses.asdict(action_param), ensure_ascii=False
|
|
185
|
+
)
|
|
186
|
+
taskid = Library.framework().MaaTaskerPostAction(
|
|
187
|
+
self._handle,
|
|
188
|
+
action_type.encode(),
|
|
189
|
+
action_param_json.encode(),
|
|
190
|
+
rect_buffer._handle,
|
|
191
|
+
reco_detail.encode(),
|
|
192
|
+
)
|
|
193
|
+
return self._gen_task_job(taskid)
|
|
194
|
+
|
|
86
195
|
@property
|
|
87
196
|
def running(self) -> bool:
|
|
197
|
+
"""判断实例是否还在运行 / Check if instance is still running
|
|
198
|
+
|
|
199
|
+
Returns:
|
|
200
|
+
bool: 是否正在运行 / Whether running
|
|
201
|
+
"""
|
|
88
202
|
return bool(Library.framework().MaaTaskerRunning(self._handle))
|
|
89
203
|
|
|
90
204
|
def post_stop(self) -> Job:
|
|
205
|
+
"""异步停止实例 / Asynchronously stop instance
|
|
206
|
+
|
|
207
|
+
这是一个异步操作,会立即返回一个 Job 对象
|
|
208
|
+
停止操作会中断当前运行的任务,并停止资源加载和控制器操作
|
|
209
|
+
This is an asynchronous operation that immediately returns a Job object
|
|
210
|
+
The stop operation will interrupt the currently running task and stop resource loading and controller operations
|
|
211
|
+
|
|
212
|
+
Returns:
|
|
213
|
+
Job: 作业对象,可通过 status/wait 查询状态 / Job object, can query status via status/wait
|
|
214
|
+
"""
|
|
91
215
|
taskid = Library.framework().MaaTaskerPostStop(self._handle)
|
|
92
216
|
return self._gen_task_job(taskid)
|
|
93
217
|
|
|
94
218
|
@property
|
|
95
219
|
def stopping(self) -> bool:
|
|
220
|
+
"""判断实例是否正在停止中(尚未停止) / Check if instance is stopping (not yet stopped)
|
|
221
|
+
|
|
222
|
+
Returns:
|
|
223
|
+
bool: 是否正在停止 / Whether stopping
|
|
224
|
+
"""
|
|
96
225
|
return bool(Library.framework().MaaTaskerStopping(self._handle))
|
|
97
226
|
|
|
98
227
|
def get_latest_node(self, name: str) -> Optional[NodeDetail]:
|
|
228
|
+
"""获取任务的最新节点号 / Get latest node id for task
|
|
229
|
+
|
|
230
|
+
Args:
|
|
231
|
+
name: 任务名 / Task name
|
|
232
|
+
|
|
233
|
+
Returns:
|
|
234
|
+
Optional[NodeDetail]: 节点详情,如果不存在则返回 None / Node detail, or None if not exists
|
|
235
|
+
"""
|
|
99
236
|
c_node_id = MaaNodeId()
|
|
100
237
|
ret = bool(
|
|
101
238
|
Library.framework().MaaTaskerGetLatestNode(
|
|
@@ -110,66 +247,90 @@ class Tasker:
|
|
|
110
247
|
return self.get_node_detail(int(c_node_id.value))
|
|
111
248
|
|
|
112
249
|
def clear_cache(self) -> bool:
|
|
250
|
+
"""清理所有可查询的信息 / Clear all queryable information
|
|
251
|
+
|
|
252
|
+
Returns:
|
|
253
|
+
bool: 是否成功 / Whether successful
|
|
254
|
+
"""
|
|
113
255
|
return bool(Library.framework().MaaTaskerClearCache(self._handle))
|
|
114
256
|
|
|
115
|
-
|
|
116
|
-
def set_log_dir(path: Union[Path, str]) -> bool:
|
|
117
|
-
strpath = str(path)
|
|
118
|
-
return bool(
|
|
119
|
-
Library.framework().MaaSetGlobalOption(
|
|
120
|
-
MaaOption(MaaGlobalOptionEnum.LogDir),
|
|
121
|
-
strpath.encode(),
|
|
122
|
-
len(strpath),
|
|
123
|
-
)
|
|
124
|
-
)
|
|
257
|
+
_sink_holder: Dict[int, "EventSink"] = {}
|
|
125
258
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
259
|
+
def add_sink(self, sink: "TaskerEventSink") -> Optional[int]:
|
|
260
|
+
"""添加实例事件监听器 / Add instance event listener
|
|
261
|
+
|
|
262
|
+
Args:
|
|
263
|
+
sink: 事件监听器 / Event sink
|
|
264
|
+
|
|
265
|
+
Returns:
|
|
266
|
+
Optional[int]: 监听器 id,失败返回 None / Listener id, or None if failed
|
|
267
|
+
"""
|
|
268
|
+
sink_id = int(
|
|
269
|
+
Library.framework().MaaTaskerAddSink(
|
|
270
|
+
self._handle, *EventSink._gen_c_param(sink)
|
|
134
271
|
)
|
|
135
272
|
)
|
|
273
|
+
if sink_id == MaaInvalidId:
|
|
274
|
+
return None
|
|
136
275
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
276
|
+
self._sink_holder[sink_id] = sink
|
|
277
|
+
return sink_id
|
|
278
|
+
|
|
279
|
+
def remove_sink(self, sink_id: int) -> None:
|
|
280
|
+
"""移除实例事件监听器 / Remove instance event listener
|
|
281
|
+
|
|
282
|
+
Args:
|
|
283
|
+
sink_id: 监听器 id / Listener id
|
|
141
284
|
"""
|
|
142
|
-
|
|
285
|
+
Library.framework().MaaTaskerRemoveSink(self._handle, sink_id)
|
|
286
|
+
self._sink_holder.pop(sink_id)
|
|
143
287
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
return bool(
|
|
148
|
-
Library.framework().MaaSetGlobalOption(
|
|
149
|
-
MaaOption(MaaGlobalOptionEnum.StdoutLevel),
|
|
150
|
-
ctypes.pointer(clevel),
|
|
151
|
-
ctypes.sizeof(MaaLoggingLevel),
|
|
152
|
-
)
|
|
153
|
-
)
|
|
288
|
+
def clear_sinks(self) -> None:
|
|
289
|
+
"""清除所有实例事件监听器 / Clear all instance event listeners"""
|
|
290
|
+
Library.framework().MaaTaskerClearSinks(self._handle)
|
|
154
291
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
292
|
+
def add_context_sink(self, sink: "ContextEventSink") -> Optional[int]:
|
|
293
|
+
"""添加上下文事件监听器 / Add context event listener
|
|
294
|
+
|
|
295
|
+
Args:
|
|
296
|
+
sink: 上下文事件监听器 / Context event sink
|
|
297
|
+
|
|
298
|
+
Returns:
|
|
299
|
+
Optional[int]: 监听器 id,失败返回 None / Listener id, or None if failed
|
|
300
|
+
"""
|
|
301
|
+
sink_id = int(
|
|
302
|
+
Library.framework().MaaTaskerAddContextSink(
|
|
303
|
+
self._handle, *EventSink._gen_c_param(sink)
|
|
163
304
|
)
|
|
164
305
|
)
|
|
306
|
+
if sink_id == MaaInvalidId:
|
|
307
|
+
return None
|
|
308
|
+
|
|
309
|
+
self._sink_holder[sink_id] = sink
|
|
310
|
+
return sink_id
|
|
311
|
+
|
|
312
|
+
def remove_context_sink(self, sink_id: int) -> None:
|
|
313
|
+
"""移除上下文事件监听器 / Remove context event listener
|
|
314
|
+
|
|
315
|
+
Args:
|
|
316
|
+
sink_id: 监听器 id / Listener id
|
|
317
|
+
"""
|
|
318
|
+
Library.framework().MaaTaskerRemoveContextSink(self._handle, sink_id)
|
|
319
|
+
self._sink_holder.pop(sink_id)
|
|
320
|
+
|
|
321
|
+
def clear_context_sinks(self) -> None:
|
|
322
|
+
"""清除所有上下文事件监听器 / Clear all context event listeners"""
|
|
323
|
+
Library.framework().MaaTaskerClearContextSinks(self._handle)
|
|
165
324
|
|
|
166
325
|
### private ###
|
|
167
326
|
|
|
168
327
|
@staticmethod
|
|
169
328
|
def _gen_post_param(entry: str, pipeline_override: Dict) -> Tuple[bytes, bytes]:
|
|
329
|
+
pipeline_json = json.dumps(pipeline_override, ensure_ascii=False)
|
|
330
|
+
|
|
170
331
|
return (
|
|
171
332
|
entry.encode(),
|
|
172
|
-
|
|
333
|
+
pipeline_json.encode(),
|
|
173
334
|
)
|
|
174
335
|
|
|
175
336
|
def _gen_task_job(self, taskid: MaaTaskId) -> JobWithResult:
|
|
@@ -187,8 +348,16 @@ class Tasker:
|
|
|
187
348
|
return Library.framework().MaaTaskerWait(self._handle, id)
|
|
188
349
|
|
|
189
350
|
def get_recognition_detail(self, reco_id: int) -> Optional[RecognitionDetail]:
|
|
351
|
+
"""获取识别信息 / Get recognition info
|
|
352
|
+
|
|
353
|
+
Args:
|
|
354
|
+
reco_id: 识别号 / Recognition id
|
|
355
|
+
|
|
356
|
+
Returns:
|
|
357
|
+
Optional[RecognitionDetail]: 识别详情,如果不存在则返回 None / Recognition detail, or None if not exists
|
|
358
|
+
"""
|
|
190
359
|
name = StringBuffer()
|
|
191
|
-
algorithm = StringBuffer()
|
|
360
|
+
algorithm = StringBuffer() # type: ignore
|
|
192
361
|
hit = MaaBool()
|
|
193
362
|
box = RectBuffer()
|
|
194
363
|
detail_json = StringBuffer()
|
|
@@ -211,25 +380,89 @@ class Tasker:
|
|
|
211
380
|
return None
|
|
212
381
|
|
|
213
382
|
raw_detail = json.loads(detail_json.get())
|
|
214
|
-
|
|
215
|
-
parsed_detail =
|
|
383
|
+
algorithm_str = algorithm.get()
|
|
384
|
+
parsed_detail = self._parse_recognition_raw_detail(algorithm_str, raw_detail)
|
|
385
|
+
|
|
386
|
+
try:
|
|
387
|
+
algorithm_enum = AlgorithmEnum(algorithm_str)
|
|
388
|
+
except ValueError:
|
|
389
|
+
algorithm_enum = algorithm_str # type: ignore
|
|
216
390
|
|
|
217
391
|
return RecognitionDetail(
|
|
218
392
|
reco_id=reco_id,
|
|
219
393
|
name=name.get(),
|
|
220
|
-
algorithm=
|
|
394
|
+
algorithm=algorithm_enum,
|
|
395
|
+
hit=bool(hit),
|
|
221
396
|
box=bool(hit) and box.get() or None,
|
|
222
397
|
all_results=parsed_detail[0],
|
|
223
|
-
|
|
398
|
+
filtered_results=parsed_detail[1],
|
|
224
399
|
best_result=parsed_detail[2],
|
|
225
400
|
raw_detail=raw_detail,
|
|
226
401
|
raw_image=raw.get(),
|
|
227
402
|
draw_images=draws.get(),
|
|
228
403
|
)
|
|
229
404
|
|
|
405
|
+
def get_action_detail(self, action_id: int) -> Optional[ActionDetail]:
|
|
406
|
+
"""获取操作信息 / Get action info
|
|
407
|
+
|
|
408
|
+
Args:
|
|
409
|
+
action_id: 操作号 / Action id
|
|
410
|
+
|
|
411
|
+
Returns:
|
|
412
|
+
Optional[ActionDetail]: 操作详情,如果不存在则返回 None / Action detail, or None if not exists
|
|
413
|
+
"""
|
|
414
|
+
name = StringBuffer()
|
|
415
|
+
action = StringBuffer()
|
|
416
|
+
box = RectBuffer()
|
|
417
|
+
c_success = MaaBool()
|
|
418
|
+
detail_json = StringBuffer()
|
|
419
|
+
|
|
420
|
+
ret = bool(
|
|
421
|
+
Library.framework().MaaTaskerGetActionDetail(
|
|
422
|
+
self._handle,
|
|
423
|
+
MaaActId(action_id),
|
|
424
|
+
name._handle,
|
|
425
|
+
action._handle,
|
|
426
|
+
box._handle,
|
|
427
|
+
ctypes.pointer(c_success),
|
|
428
|
+
detail_json._handle,
|
|
429
|
+
)
|
|
430
|
+
)
|
|
431
|
+
|
|
432
|
+
if not ret:
|
|
433
|
+
return None
|
|
434
|
+
|
|
435
|
+
raw_detail = json.loads(detail_json.get())
|
|
436
|
+
action_str = action.get()
|
|
437
|
+
parsed_result = Tasker._parse_action_raw_detail(action_str, raw_detail)
|
|
438
|
+
|
|
439
|
+
try:
|
|
440
|
+
action_enum = ActionEnum(action_str)
|
|
441
|
+
except ValueError:
|
|
442
|
+
action_enum = action_str # type: ignore
|
|
443
|
+
|
|
444
|
+
return ActionDetail(
|
|
445
|
+
action_id=action_id,
|
|
446
|
+
name=name.get(),
|
|
447
|
+
action=action_enum,
|
|
448
|
+
box=box.get(),
|
|
449
|
+
success=bool(c_success),
|
|
450
|
+
result=parsed_result,
|
|
451
|
+
raw_detail=raw_detail,
|
|
452
|
+
)
|
|
453
|
+
|
|
230
454
|
def get_node_detail(self, node_id: int) -> Optional[NodeDetail]:
|
|
455
|
+
"""获取节点信息 / Get node info
|
|
456
|
+
|
|
457
|
+
Args:
|
|
458
|
+
node_id: 节点号 / Node id
|
|
459
|
+
|
|
460
|
+
Returns:
|
|
461
|
+
Optional[NodeDetail]: 节点详情,如果不存在则返回 None / Node detail, or None if not exists
|
|
462
|
+
"""
|
|
231
463
|
name = StringBuffer()
|
|
232
464
|
c_reco_id = MaaRecoId()
|
|
465
|
+
c_action_id = MaaActId()
|
|
233
466
|
c_completed = MaaBool()
|
|
234
467
|
|
|
235
468
|
ret = bool(
|
|
@@ -238,6 +471,7 @@ class Tasker:
|
|
|
238
471
|
MaaNodeId(node_id),
|
|
239
472
|
name._handle,
|
|
240
473
|
ctypes.pointer(c_reco_id),
|
|
474
|
+
ctypes.pointer(c_action_id),
|
|
241
475
|
ctypes.pointer(c_completed),
|
|
242
476
|
)
|
|
243
477
|
)
|
|
@@ -245,18 +479,34 @@ class Tasker:
|
|
|
245
479
|
if not ret:
|
|
246
480
|
return None
|
|
247
481
|
|
|
248
|
-
recognition =
|
|
249
|
-
|
|
250
|
-
|
|
482
|
+
recognition = (
|
|
483
|
+
self.get_recognition_detail(int(c_reco_id.value))
|
|
484
|
+
if c_reco_id.value != 0
|
|
485
|
+
else None
|
|
486
|
+
)
|
|
487
|
+
action = (
|
|
488
|
+
self.get_action_detail(int(c_action_id.value))
|
|
489
|
+
if c_action_id.value != 0
|
|
490
|
+
else None
|
|
491
|
+
)
|
|
251
492
|
|
|
252
493
|
return NodeDetail(
|
|
253
494
|
node_id=node_id,
|
|
254
495
|
name=name.get(),
|
|
255
496
|
recognition=recognition,
|
|
497
|
+
action=action,
|
|
256
498
|
completed=bool(c_completed),
|
|
257
499
|
)
|
|
258
500
|
|
|
259
501
|
def get_task_detail(self, task_id: int) -> Optional[TaskDetail]:
|
|
502
|
+
"""获取任务信息 / Get task info
|
|
503
|
+
|
|
504
|
+
Args:
|
|
505
|
+
task_id: 任务号 / Task id
|
|
506
|
+
|
|
507
|
+
Returns:
|
|
508
|
+
Optional[TaskDetail]: 任务详情,如果不存在则返回 None / Task detail, or None if not exists
|
|
509
|
+
"""
|
|
260
510
|
size = MaaSize()
|
|
261
511
|
entry = StringBuffer()
|
|
262
512
|
status = MaaStatus()
|
|
@@ -296,33 +546,239 @@ class Tasker:
|
|
|
296
546
|
task_id=task_id, entry=entry.get(), nodes=nodes, status=Status(status)
|
|
297
547
|
)
|
|
298
548
|
|
|
299
|
-
|
|
549
|
+
@staticmethod
|
|
550
|
+
def set_log_dir(path: Union[Path, str]) -> bool:
|
|
551
|
+
"""设置日志路径 / Set the log path
|
|
552
|
+
|
|
553
|
+
Args:
|
|
554
|
+
path: 日志路径 / Log path
|
|
555
|
+
|
|
556
|
+
Returns:
|
|
557
|
+
bool: 是否成功 / Whether successful
|
|
558
|
+
"""
|
|
559
|
+
strpath = str(path)
|
|
560
|
+
return bool(
|
|
561
|
+
Library.framework().MaaGlobalSetOption(
|
|
562
|
+
MaaOption(MaaGlobalOptionEnum.LogDir),
|
|
563
|
+
strpath.encode(),
|
|
564
|
+
len(strpath),
|
|
565
|
+
)
|
|
566
|
+
)
|
|
567
|
+
|
|
568
|
+
@staticmethod
|
|
569
|
+
def set_save_draw(save_draw: bool) -> bool:
|
|
570
|
+
"""设置是否将识别保存到日志路径/vision中 / Set whether to save recognition results to log path/vision
|
|
571
|
+
|
|
572
|
+
开启后 RecoDetail 将可以获取到 draws / When enabled, RecoDetail can retrieve draws
|
|
573
|
+
|
|
574
|
+
Args:
|
|
575
|
+
save_draw: 是否保存 / Whether to save
|
|
576
|
+
|
|
577
|
+
Returns:
|
|
578
|
+
bool: 是否成功 / Whether successful
|
|
579
|
+
"""
|
|
580
|
+
cbool = ctypes.c_bool(save_draw)
|
|
581
|
+
return bool(
|
|
582
|
+
Library.framework().MaaGlobalSetOption(
|
|
583
|
+
MaaOption(MaaGlobalOptionEnum.SaveDraw),
|
|
584
|
+
ctypes.pointer(cbool),
|
|
585
|
+
ctypes.sizeof(ctypes.c_bool),
|
|
586
|
+
)
|
|
587
|
+
)
|
|
588
|
+
|
|
589
|
+
@staticmethod
|
|
590
|
+
def set_recording(recording: bool) -> bool:
|
|
591
|
+
"""
|
|
592
|
+
Deprecated
|
|
593
|
+
"""
|
|
594
|
+
return False
|
|
595
|
+
|
|
596
|
+
@staticmethod
|
|
597
|
+
def set_stdout_level(level: LoggingLevelEnum) -> bool:
|
|
598
|
+
"""设置日志输出到 stdout 中的级别 / Set the log output level to stdout
|
|
599
|
+
|
|
600
|
+
Args:
|
|
601
|
+
level: 日志级别 / Logging level
|
|
602
|
+
|
|
603
|
+
Returns:
|
|
604
|
+
bool: 是否成功 / Whether successful
|
|
605
|
+
"""
|
|
606
|
+
clevel = MaaLoggingLevel(level)
|
|
607
|
+
return bool(
|
|
608
|
+
Library.framework().MaaGlobalSetOption(
|
|
609
|
+
MaaOption(MaaGlobalOptionEnum.StdoutLevel),
|
|
610
|
+
ctypes.pointer(clevel),
|
|
611
|
+
ctypes.sizeof(MaaLoggingLevel),
|
|
612
|
+
)
|
|
613
|
+
)
|
|
614
|
+
|
|
615
|
+
@staticmethod
|
|
616
|
+
def set_debug_mode(debug_mode: bool) -> bool:
|
|
617
|
+
"""设置是否启用调试模式 / Set whether to enable debug mode
|
|
618
|
+
|
|
619
|
+
调试模式下, RecoDetail 将可以获取到 raw/draws; 所有任务都会被视为 focus 而产生回调
|
|
620
|
+
In debug mode, RecoDetail can retrieve raw/draws; all tasks are treated as focus and produce callbacks
|
|
621
|
+
|
|
622
|
+
Args:
|
|
623
|
+
debug_mode: 是否启用调试模式 / Whether to enable debug mode
|
|
624
|
+
|
|
625
|
+
Returns:
|
|
626
|
+
bool: 是否成功 / Whether successful
|
|
627
|
+
"""
|
|
628
|
+
cbool = ctypes.c_bool(debug_mode)
|
|
629
|
+
return bool(
|
|
630
|
+
Library.framework().MaaGlobalSetOption(
|
|
631
|
+
MaaOption(MaaGlobalOptionEnum.DebugMode),
|
|
632
|
+
ctypes.pointer(cbool),
|
|
633
|
+
ctypes.sizeof(ctypes.c_bool),
|
|
634
|
+
)
|
|
635
|
+
)
|
|
636
|
+
|
|
637
|
+
@staticmethod
|
|
638
|
+
def set_save_on_error(save_on_error: bool) -> bool:
|
|
639
|
+
"""设置是否在错误时保存截图到日志路径/on_error中 / Set whether to save screenshot on error to log path/on_error
|
|
640
|
+
|
|
641
|
+
Args:
|
|
642
|
+
save_on_error: 是否保存 / Whether to save
|
|
643
|
+
|
|
644
|
+
Returns:
|
|
645
|
+
bool: 是否成功 / Whether successful
|
|
646
|
+
"""
|
|
647
|
+
cbool = ctypes.c_bool(save_on_error)
|
|
648
|
+
return bool(
|
|
649
|
+
Library.framework().MaaGlobalSetOption(
|
|
650
|
+
MaaOption(MaaGlobalOptionEnum.SaveOnError),
|
|
651
|
+
ctypes.pointer(cbool),
|
|
652
|
+
ctypes.sizeof(ctypes.c_bool),
|
|
653
|
+
)
|
|
654
|
+
)
|
|
300
655
|
|
|
301
656
|
@staticmethod
|
|
302
|
-
def
|
|
657
|
+
def set_draw_quality(quality: int) -> bool:
|
|
658
|
+
"""设置识别可视化图像的 JPEG 质量 / Set the JPEG quality for recognition visualization images
|
|
659
|
+
|
|
660
|
+
Args:
|
|
661
|
+
quality: JPEG 质量(0-100),默认 85 / JPEG quality (0-100), default 85
|
|
662
|
+
|
|
663
|
+
Returns:
|
|
664
|
+
bool: 是否成功 / Whether successful
|
|
665
|
+
"""
|
|
666
|
+
cquality = ctypes.c_int(quality)
|
|
667
|
+
return bool(
|
|
668
|
+
Library.framework().MaaGlobalSetOption(
|
|
669
|
+
MaaOption(MaaGlobalOptionEnum.DrawQuality),
|
|
670
|
+
ctypes.pointer(cquality),
|
|
671
|
+
ctypes.sizeof(ctypes.c_int),
|
|
672
|
+
)
|
|
673
|
+
)
|
|
674
|
+
|
|
675
|
+
@staticmethod
|
|
676
|
+
def set_reco_image_cache_limit(limit: int) -> bool:
|
|
677
|
+
"""设置识别图像缓存数量限制 / Set the recognition image cache limit
|
|
678
|
+
|
|
679
|
+
Args:
|
|
680
|
+
limit: 缓存数量限制,默认 4096 / Cache limit, default 4096
|
|
681
|
+
|
|
682
|
+
Returns:
|
|
683
|
+
bool: 是否成功 / Whether successful
|
|
684
|
+
"""
|
|
685
|
+
climit = ctypes.c_size_t(limit)
|
|
686
|
+
return bool(
|
|
687
|
+
Library.framework().MaaGlobalSetOption(
|
|
688
|
+
MaaOption(MaaGlobalOptionEnum.RecoImageCacheLimit),
|
|
689
|
+
ctypes.pointer(climit),
|
|
690
|
+
ctypes.sizeof(ctypes.c_size_t),
|
|
691
|
+
)
|
|
692
|
+
)
|
|
693
|
+
|
|
694
|
+
@staticmethod
|
|
695
|
+
def load_plugin(path: Union[Path, str]) -> bool:
|
|
696
|
+
"""加载插件 / Load plugin
|
|
697
|
+
|
|
698
|
+
可以使用完整路径或仅使用名称, 仅使用名称时会在系统目录和当前目录中搜索. 也可以递归搜索目录中的插件
|
|
699
|
+
Can use full path or name only. When using name only, will search in system directory and current directory. Can also recursively search for plugins in a directory
|
|
700
|
+
|
|
701
|
+
Args:
|
|
702
|
+
path: 插件库路径或名称 / Plugin library path or name
|
|
703
|
+
|
|
704
|
+
Returns:
|
|
705
|
+
bool: 是否成功 / Whether successful
|
|
706
|
+
"""
|
|
707
|
+
strpath = str(path)
|
|
708
|
+
return bool(
|
|
709
|
+
Library.framework().MaaGlobalLoadPlugin(
|
|
710
|
+
strpath.encode(),
|
|
711
|
+
)
|
|
712
|
+
)
|
|
713
|
+
|
|
714
|
+
_api_properties_initialized: bool = False
|
|
715
|
+
|
|
716
|
+
def _parse_recognition_raw_detail(self, algorithm: str, raw_detail):
|
|
303
717
|
if not raw_detail:
|
|
304
718
|
return [], [], None
|
|
305
719
|
|
|
306
|
-
|
|
720
|
+
try:
|
|
721
|
+
algorithm_enum = AlgorithmEnum(algorithm)
|
|
722
|
+
except ValueError:
|
|
723
|
+
return [], [], None
|
|
724
|
+
|
|
725
|
+
ResultType = AlgorithmResultDict.get(algorithm_enum)
|
|
307
726
|
if not ResultType:
|
|
308
727
|
return [], [], None
|
|
309
728
|
|
|
729
|
+
# And/Or 的 detail 是子识别结果数组,递归获取完整的 RecognitionDetail
|
|
730
|
+
if algorithm_enum in (AlgorithmEnum.And, AlgorithmEnum.Or):
|
|
731
|
+
sub_results = []
|
|
732
|
+
for sub in raw_detail:
|
|
733
|
+
reco_id = sub.get("reco_id")
|
|
734
|
+
if not reco_id:
|
|
735
|
+
continue
|
|
736
|
+
sub_detail = self.get_recognition_detail(reco_id)
|
|
737
|
+
if sub_detail:
|
|
738
|
+
sub_results.append(sub_detail)
|
|
739
|
+
result = ResultType(sub_results=sub_results)
|
|
740
|
+
return [result], [result], result
|
|
741
|
+
|
|
310
742
|
all_results: List[RecognitionResult] = []
|
|
311
|
-
|
|
743
|
+
filtered_results: List[RecognitionResult] = []
|
|
312
744
|
best_result: Optional[RecognitionResult] = None
|
|
313
745
|
|
|
314
746
|
raw_all_results = raw_detail.get("all", [])
|
|
315
|
-
|
|
747
|
+
raw_filtered_results = raw_detail.get("filtered", [])
|
|
316
748
|
raw_best_result = raw_detail.get("best", None)
|
|
317
749
|
|
|
318
750
|
for raw_result in raw_all_results:
|
|
319
751
|
all_results.append(ResultType(**raw_result))
|
|
320
|
-
for raw_result in
|
|
321
|
-
|
|
752
|
+
for raw_result in raw_filtered_results:
|
|
753
|
+
filtered_results.append(ResultType(**raw_result))
|
|
322
754
|
if raw_best_result:
|
|
323
755
|
best_result = ResultType(**raw_best_result)
|
|
324
756
|
|
|
325
|
-
return all_results,
|
|
757
|
+
return all_results, filtered_results, best_result
|
|
758
|
+
|
|
759
|
+
@staticmethod
|
|
760
|
+
def _parse_action_raw_detail(
|
|
761
|
+
action: str, raw_detail: Dict
|
|
762
|
+
) -> Optional[ActionResult]:
|
|
763
|
+
if not raw_detail:
|
|
764
|
+
return None
|
|
765
|
+
|
|
766
|
+
try:
|
|
767
|
+
action_enum = ActionEnum(action)
|
|
768
|
+
except ValueError:
|
|
769
|
+
return None
|
|
770
|
+
|
|
771
|
+
ResultType = ActionResultDict.get(action_enum)
|
|
772
|
+
if not ResultType:
|
|
773
|
+
return None
|
|
774
|
+
|
|
775
|
+
try:
|
|
776
|
+
# cv::Point 在 JSON 中是数组 [x, y],不需要转换
|
|
777
|
+
# 直接使用 raw_detail 创建结果对象
|
|
778
|
+
return ResultType(**raw_detail)
|
|
779
|
+
except (TypeError, KeyError):
|
|
780
|
+
# 如果解析失败,返回 None
|
|
781
|
+
return None
|
|
326
782
|
|
|
327
783
|
@staticmethod
|
|
328
784
|
def _set_api_properties():
|
|
@@ -330,12 +786,21 @@ class Tasker:
|
|
|
330
786
|
return
|
|
331
787
|
Tasker._api_properties_initialized = True
|
|
332
788
|
|
|
333
|
-
Library.framework().
|
|
334
|
-
Library.framework().
|
|
335
|
-
|
|
336
|
-
|
|
789
|
+
Library.framework().MaaGlobalSetOption.restype = MaaBool
|
|
790
|
+
Library.framework().MaaGlobalSetOption.argtypes = [
|
|
791
|
+
MaaGlobalOption,
|
|
792
|
+
MaaOptionValue,
|
|
793
|
+
MaaOptionValueSize,
|
|
794
|
+
]
|
|
795
|
+
|
|
796
|
+
Library.framework().MaaGlobalLoadPlugin.restype = MaaBool
|
|
797
|
+
Library.framework().MaaGlobalLoadPlugin.argtypes = [
|
|
798
|
+
ctypes.c_char_p,
|
|
337
799
|
]
|
|
338
800
|
|
|
801
|
+
Library.framework().MaaTaskerCreate.restype = MaaTaskerHandle
|
|
802
|
+
Library.framework().MaaTaskerCreate.argtypes = []
|
|
803
|
+
|
|
339
804
|
Library.framework().MaaTaskerDestroy.restype = None
|
|
340
805
|
Library.framework().MaaTaskerDestroy.argtypes = [MaaTaskerHandle]
|
|
341
806
|
|
|
@@ -361,6 +826,23 @@ class Tasker:
|
|
|
361
826
|
ctypes.c_char_p,
|
|
362
827
|
]
|
|
363
828
|
|
|
829
|
+
Library.framework().MaaTaskerPostRecognition.restype = MaaId
|
|
830
|
+
Library.framework().MaaTaskerPostRecognition.argtypes = [
|
|
831
|
+
MaaTaskerHandle,
|
|
832
|
+
ctypes.c_char_p,
|
|
833
|
+
ctypes.c_char_p,
|
|
834
|
+
MaaImageBufferHandle,
|
|
835
|
+
]
|
|
836
|
+
|
|
837
|
+
Library.framework().MaaTaskerPostAction.restype = MaaId
|
|
838
|
+
Library.framework().MaaTaskerPostAction.argtypes = [
|
|
839
|
+
MaaTaskerHandle,
|
|
840
|
+
ctypes.c_char_p,
|
|
841
|
+
ctypes.c_char_p,
|
|
842
|
+
MaaRectHandle,
|
|
843
|
+
ctypes.c_char_p,
|
|
844
|
+
]
|
|
845
|
+
|
|
364
846
|
Library.framework().MaaTaskerStatus.restype = MaaStatus
|
|
365
847
|
Library.framework().MaaTaskerStatus.argtypes = [
|
|
366
848
|
MaaTaskerHandle,
|
|
@@ -401,12 +883,24 @@ class Tasker:
|
|
|
401
883
|
MaaImageListBufferHandle,
|
|
402
884
|
]
|
|
403
885
|
|
|
886
|
+
Library.framework().MaaTaskerGetActionDetail.restype = MaaBool
|
|
887
|
+
Library.framework().MaaTaskerGetActionDetail.argtypes = [
|
|
888
|
+
MaaTaskerHandle,
|
|
889
|
+
MaaActId,
|
|
890
|
+
MaaStringBufferHandle,
|
|
891
|
+
MaaStringBufferHandle,
|
|
892
|
+
MaaRectHandle,
|
|
893
|
+
ctypes.POINTER(MaaBool),
|
|
894
|
+
MaaStringBufferHandle,
|
|
895
|
+
]
|
|
896
|
+
|
|
404
897
|
Library.framework().MaaTaskerGetNodeDetail.restype = MaaBool
|
|
405
898
|
Library.framework().MaaTaskerGetNodeDetail.argtypes = [
|
|
406
899
|
MaaTaskerHandle,
|
|
407
900
|
MaaNodeId,
|
|
408
901
|
MaaStringBufferHandle,
|
|
409
902
|
ctypes.POINTER(MaaRecoId),
|
|
903
|
+
ctypes.POINTER(MaaActId),
|
|
410
904
|
ctypes.POINTER(MaaBool),
|
|
411
905
|
]
|
|
412
906
|
|
|
@@ -432,9 +926,70 @@ class Tasker:
|
|
|
432
926
|
MaaTaskerHandle,
|
|
433
927
|
]
|
|
434
928
|
|
|
435
|
-
Library.framework().
|
|
436
|
-
Library.framework().
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
929
|
+
Library.framework().MaaTaskerAddSink.restype = MaaSinkId
|
|
930
|
+
Library.framework().MaaTaskerAddSink.argtypes = [
|
|
931
|
+
MaaTaskerHandle,
|
|
932
|
+
MaaEventCallback,
|
|
933
|
+
ctypes.c_void_p,
|
|
934
|
+
]
|
|
935
|
+
|
|
936
|
+
Library.framework().MaaTaskerRemoveSink.restype = None
|
|
937
|
+
Library.framework().MaaTaskerRemoveSink.argtypes = [
|
|
938
|
+
MaaTaskerHandle,
|
|
939
|
+
MaaSinkId,
|
|
940
|
+
]
|
|
941
|
+
|
|
942
|
+
Library.framework().MaaTaskerClearSinks.restype = None
|
|
943
|
+
Library.framework().MaaTaskerClearSinks.argtypes = [MaaTaskerHandle]
|
|
944
|
+
|
|
945
|
+
Library.framework().MaaTaskerAddContextSink.restype = MaaSinkId
|
|
946
|
+
Library.framework().MaaTaskerAddContextSink.argtypes = [
|
|
947
|
+
MaaTaskerHandle,
|
|
948
|
+
MaaEventCallback,
|
|
949
|
+
ctypes.c_void_p,
|
|
950
|
+
]
|
|
951
|
+
|
|
952
|
+
Library.framework().MaaTaskerRemoveContextSink.restype = None
|
|
953
|
+
Library.framework().MaaTaskerRemoveContextSink.argtypes = [
|
|
954
|
+
MaaTaskerHandle,
|
|
955
|
+
MaaSinkId,
|
|
440
956
|
]
|
|
957
|
+
|
|
958
|
+
Library.framework().MaaTaskerClearContextSinks.restype = None
|
|
959
|
+
Library.framework().MaaTaskerClearContextSinks.argtypes = [MaaTaskerHandle]
|
|
960
|
+
|
|
961
|
+
|
|
962
|
+
class TaskerEventSink(EventSink):
|
|
963
|
+
|
|
964
|
+
@dataclass
|
|
965
|
+
class TaskerTaskDetail:
|
|
966
|
+
task_id: int
|
|
967
|
+
entry: str
|
|
968
|
+
uuid: str
|
|
969
|
+
hash: str
|
|
970
|
+
|
|
971
|
+
def on_tasker_task(
|
|
972
|
+
self, tasker: Tasker, noti_type: NotificationType, detail: TaskerTaskDetail
|
|
973
|
+
):
|
|
974
|
+
pass
|
|
975
|
+
|
|
976
|
+
def on_raw_notification(self, tasker: Tasker, msg: str, details: dict):
|
|
977
|
+
pass
|
|
978
|
+
|
|
979
|
+
def _on_raw_notification(self, handle: ctypes.c_void_p, msg: str, details: dict):
|
|
980
|
+
|
|
981
|
+
tasker = Tasker(handle=handle)
|
|
982
|
+
self.on_raw_notification(tasker, msg, details)
|
|
983
|
+
|
|
984
|
+
noti_type = EventSink._notification_type(msg)
|
|
985
|
+
if msg.startswith("Tasker.Task"):
|
|
986
|
+
detail = self.TaskerTaskDetail(
|
|
987
|
+
task_id=details["task_id"],
|
|
988
|
+
entry=details["entry"],
|
|
989
|
+
uuid=details["uuid"],
|
|
990
|
+
hash=details["hash"],
|
|
991
|
+
)
|
|
992
|
+
self.on_tasker_task(tasker, noti_type, detail)
|
|
993
|
+
|
|
994
|
+
else:
|
|
995
|
+
self.on_unknown_notification(tasker, msg, details)
|