# 2.3 回调协议

MaaFramework 通过 `MaaEventCallback` 回调函数向上层应用发送各种状态通知和事件消息。所有回调消息都采用统一的格式：消息类型（message）+ 详细数据（details）。

## 消息格式

```cpp
typedef void(MAA_CALL* MaaEventCallback)(void* handle, const char* message, const char* details_json, void* trans_arg);
```

- **handle**: 相关对象的句柄
  - MaaTasker* for MaaTasker event
  - MaaResource* for MaaResource event
  - MaaController* for MaaController event
  - MaaContext* for MaaContext event
- **message**: 消息类型字符串，标识事件类型
- **details_json**: JSON 格式的详细数据，包含具体的事件信息
- **callback_arg**: 用户自定义的回调参数

## 消息类型

### 资源加载消息

用于通知资源加载的状态变化。

#### `Resource.Loading.Starting`

资源开始加载时发送。

**details_json 结构：**

```json
{
    "res_id": 12345,
    "path": "/path/to/resource",
    "type": "Bundle",
    "hash": "abc123def456"
}
```

- `res_id`: 资源 ID（数字）
- `path`: 资源路径（字符串）
- `type`: 加载类型（字符串）
  - `"Bundle"`: 完整资源目录（通过 `post_bundle` 加载）
  - `"OcrModel"`: OCR 模型目录（通过 `post_ocr_model` 加载）
  - `"Pipeline"`: Pipeline 目录或单个 json/jsonc 文件（通过 `post_pipeline` 加载）
  - `"Image"`: 图片目录或单个图片文件（通过 `post_image` 加载）
- `hash`: 资源哈希值（字符串）

#### `Resource.Loading.Succeeded`

资源加载成功时发送。数据结构同上。

#### `Resource.Loading.Failed`

资源加载失败时发送。数据结构同上。

### 控制器动作消息

用于通知控制器执行动作的状态。

#### `Controller.Action.Starting`

控制器开始执行动作时发送。

**details_json 结构：**

```json
{
    "ctrl_id": 12345,
    "uuid": "550e8400-e29b-41d4-a716-446655440000",
    "action": "Click",
    "param": {
        "x": 100,
        "y": 200
    },
    "info": {
        "type": "adb",
        "adb_path": "/usr/bin/adb",
        "adb_serial": "127.0.0.1:5555"
    }
}
```

- `ctrl_id`: 控制器 ID（数字）
- `uuid`: 唯一标识符（字符串）
- `action`: 动作类型（字符串）
- `param`: 动作参数（对象）
- `info`: 控制器信息（对象），参见 [MaaControllerGetInfo](2.2-集成接口一览.md#maacontrollergetinfo)

#### `Controller.Action.Succeeded`

控制器动作执行成功时发送。数据结构同上。

#### `Controller.Action.Failed`

控制器动作执行失败时发送。数据结构同上。

### 任务消息

用于通知任务执行的状态。

#### `Tasker.Task.Starting`

任务开始执行时发送。

**details_json 结构：**

```json
{
    "task_id": 12345,
    "entry": "MyTask",
    "uuid": "550e8400-e29b-41d4-a716-446655440000",
    "hash": "abc123def456"
}
```

- `task_id`: 任务 ID（数字）
- `entry`: 入口任务名称（字符串）
- `uuid`: 唯一标识符（字符串）
- `hash`: 任务哈希值（字符串）

#### `Tasker.Task.Succeeded`

任务执行成功时发送。数据结构同上。

#### `Tasker.Task.Failed`

任务执行失败时发送。数据结构同上。

### 节点下一步列表消息

用于通知节点识别下一步节点列表。

#### `Node.NextList.Starting`

节点开始识别下一步节点列表时发送。

**details_json 结构：**

```jsonc
{
    "task_id": 12345,
    "name": "NodeA",
    "list": [
        {
            "name": "NodeB",
            "jump_back": false,
            "anchor": false
        },
        {
            "name": "NodeC",
            "jump_back": true,
            "anchor": false
        },
        {
            "name": "LastHandler",
            "jump_back": false,
            "anchor": true
        }
    ],
    "focus": any,
}
```

- `task_id`: 任务 ID（数字）
- `name`: 节点名称（字符串）
- `list`: 下一步节点列表（对象数组）
  - `name`: 节点名称或锚点名称（字符串）
  - `jump_back`: 是否回跳（布尔值）
  - `anchor`: 是否为锚点引用（布尔值），若为 true 则 name 为锚点名称
- `focus`: 焦点相关数据（任意类型）

#### `Node.NextList.Succeeded`

节点成功识别下一步节点列表时发送。数据结构同上。

#### `Node.NextList.Failed`

节点识别下一步节点列表失败时发送。数据结构同上。

### 节点识别消息

用于通知节点识别过程的状态。

#### `Node.Recognition.Starting`

节点开始识别时发送。

**details_json 结构：**

```jsonc
{
    "task_id": 12345,
    "reco_id": 67890,
    "name": "NodeA",
    "focus": any,
    "anchor": "AnchorName", // 可选
}
```

- `task_id`: 任务 ID（数字）
- `reco_id`: 识别 ID（数字）
- `name`: 节点名称（字符串）
- `focus`: 焦点相关数据（任意类型）
- `anchor`: 锚点引用名称（字符串，可选）。当该识别是通过 `[Anchor]` 节点属性引用发起时存在，值为锚点名称

#### `Node.Recognition.Succeeded`

节点识别成功时发送。数据结构同上。

#### `Node.Recognition.Failed`

节点识别失败时发送。数据结构同上。

### 节点动作消息

用于通知节点执行动作的状态。

#### `Node.Action.Starting`

节点开始执行动作时发送。

**details_json 结构：**

```jsonc
{
    "task_id": 12345,
    "node_id": 67890,
    "action_id": 11111,
    "name": "NodeA",
    "focus": any,
}
```

- `task_id`: 任务 ID（数字）
- `action_id`: 操作 ID（数字）
- `name`: 节点名称（字符串）
- `focus`: 焦点相关数据（任意类型）

#### `Node.Action.Succeeded`

节点动作执行成功时发送。数据结构同上。

#### `Node.Action.Failed`

节点动作执行失败时发送。数据结构同上。

### 等待画面静止消息

用于通知 `wait_freezes` 的执行状态（pre/post/repeat/context 阶段）。

#### `Node.WaitFreezes.Starting`

开始执行 `wait_freezes` 时发送。

**details_json 结构：**

```jsonc
{
    "task_id": 12345,
    "wf_id": 600000001,
    "name": "NodeA",
    "phase": "pre",
    "roi": [0, 0, 1280, 720],
    "param": {
        "time": 3000,
        "threshold": 0.95,
        "method": 5,
        "rate_limit": 1000,
        "timeout": 20000
    },
    "focus": any,
}
```

- `task_id`: 任务 ID（数字）
- `wf_id`: Wait Freezes ID（数字），可通过 `MaaTaskerGetWaitFreezesDetail` 查询详情
- `name`: 节点名称（字符串），`Context.wait_freezes` 场景下为空字符串
- `phase`: 等待阶段（字符串）
  - `"pre"`: `pre_wait_freezes`
  - `"post"`: `post_wait_freezes`
  - `"repeat"`: `repeat_wait_freezes`
  - `"context"`: `Context.wait_freezes`
- `roi`: 实际比较的 ROI 区域（`[x, y, width, height]`）
- `param`: 等待参数（对象）
  - `time`: 要求静止时长（毫秒，数字）
  - `threshold`: 比较阈值（数字）
  - `method`: OpenCV 模板匹配方法（数字）
  - `rate_limit`: 采样间隔（毫秒，数字）
  - `timeout`: 超时时间（毫秒，数字），负数表示无限等待
- `focus`: 焦点相关数据（任意类型）

#### `Node.WaitFreezes.Succeeded`

`wait_freezes` 执行成功时发送。

相比 `Starting`，额外包含：

- `reco_ids`: 每次画面对比生成的识别 ID 列表（数字数组）
- `elapsed`: 总耗时（毫秒，数字）

#### `Node.WaitFreezes.Failed`

`wait_freezes` 执行失败时发送（例如超时、截图为空等）。

数据结构同 `Succeeded`。

### 流水线节点消息

用于通知流水线节点执行的状态。流水线节点消息在通过 `post_task` 或 `run_task` 执行完整流水线任务时发送。

#### `Node.PipelineNode.Starting`

流水线节点开始执行时发送。

**details_json 结构：**

```jsonc
{
    "task_id": 12345,
    "node_id": 67890,
    "name": "NodeA",
    "focus": any,
}
```

- `task_id`: 任务 ID（数字）
- `node_id`: 节点 ID（数字）
- `name`: 节点名称（字符串）
- `focus`: 焦点相关数据（任意类型）

#### `Node.PipelineNode.Succeeded`

流水线节点执行成功时发送。数据结构同上。

#### `Node.PipelineNode.Failed`

流水线节点执行失败时发送。数据结构同上。

### 识别节点消息

用于通知识别节点执行的状态。识别节点消息仅在通过 `run_recognition` 执行仅识别任务时发送。

#### `Node.RecognitionNode.Starting`

识别节点开始执行时发送。

**details_json 结构：**

```jsonc
{
    "task_id": 12345,
    "node_id": 67890,
    "name": "NodeA",
    "focus": any,
}
```

- `task_id`: 任务 ID（数字）
- `node_id`: 节点 ID（数字）
- `name`: 节点名称（字符串）
- `focus`: 焦点相关数据（任意类型）

#### `Node.RecognitionNode.Succeeded`

识别节点执行成功时发送。数据结构同上。

#### `Node.RecognitionNode.Failed`

识别节点执行失败时发送。数据结构同上。

### 动作节点消息

用于通知动作节点执行的状态。动作节点消息仅在通过 `run_action` 执行仅动作任务时发送。

#### `Node.ActionNode.Starting`

动作节点开始执行时发送。

**details_json 结构：**

```jsonc
{
    "task_id": 12345,
    "node_id": 67890,
    "name": "NodeA",
    "focus": any,
}
```

- `task_id`: 任务 ID（数字）
- `node_id`: 节点 ID（数字）
- `name`: 节点名称（字符串）
- `focus`: 焦点相关数据（任意类型）

#### `Node.ActionNode.Succeeded`

动作节点执行成功时发送。数据结构同上。

#### `Node.ActionNode.Failed`

动作节点执行失败时发送。数据结构同上。

## 使用示例

```cpp
void MyCallback(void* handle, const char* message, const char* details_json, void* callback_arg)
{
    // 解析消息类型
    if (strcmp(message, "Tasker.Task.Starting") == 0) {
        // 解析 JSON 获取任务详情
        // 更新 UI 显示任务开始状态
    }
    else if (strcmp(message, "Node.Recognition.Succeeded") == 0) {
        // 处理识别成功事件
        // 更新识别结果显示
    }
    // ... 处理其他消息类型
}

// 设置回调
MaaTaskerAddSink(tasker, MyCallback, nullptr);
MaaTaskerAddNodeSink(tasker, MyCallback, nullptr);
```

## 注意事项

1. **JSON 解析**: `details_json` 参数始终为有效的 JSON 字符串，建议使用成熟的 JSON 库进行解析
2. **线程安全**: 回调函数可能在不同线程中被调用，需要注意线程安全
3. **性能考虑**: 回调函数应尽快返回，避免阻塞框架的执行流程
4. **错误处理**: 建议在回调函数中添加异常处理，防止回调函数异常影响框架运行
