# 自定义AT开发指南 ***Copyright © Quectel Wireless Solutions Co., Ltd. 2026. All rights reserved.*** --- # 功能概述 AT命令(Attention Command)是一种用于控制调制解调器或通信模块的标准化指令集。每条命令以“AT”开头,用于通知设备即将发送命令,随后设备执行具体操作,如查询信号质量、发送短信、拨打电话或配置模块参数。设备执行命令后会返回响应,如 **OK**、**ERROR** 或状态信息,响应末尾通常带有回车换行(CR/LF)作为格式化标记。 AT命令广泛应用于GSM、3G/4G/5G模块、蓝牙和GPS设备,支持文本与数字两种响应模式,便于开发者进行设备控制、状态查询和调试。由于其简洁性、标准化和跨厂商兼容性,AT命令成为嵌入式通信和模块开发中最常用的控制接口。本文主要描述如何通过相关API实现自定义AT命令的注册与处理。 ## AT命令类型 标准AT命令一般有 **四种基本操作类型:测试命令、查询命令、设置命令** 和 **执行命令**。 |

类型
|

示例
|

说明
| | --- | --- | --- | | **测试命令** | **AT+``=?** | 测试是否存在相应的命令,并返回有关其参数的类型、值或范围的信息 | | **查询命令** | **AT+``?** | 查询相应命令的当前参数值 | | **设置命令** | **AT+``=``[,``[,``[...]]]** | 设置用户可定义的参数值 | | **执行命令** | **AT+``** | 返回特定的参数信息或执行特定的操作 | ### 测试命令 - **格式** ```bash AT+=? ``` - **作用** 测试是否存在相应的命令,并返回有关其参数的类型、值或范围的信息。 - **示例** ```objective-c AT+CFUN=? +CFUN: (0,1,4) OK ``` 上述示例表示该命令支持0、1和4这三种模式。 - **特点** - 以=?结尾; - 查询可支持的参数范围; - 常用于调试或开发阶段。 ### 查询命令 - **格式** ```bash AT+? ``` - **作用** 查询相应命令的当前参数值。 - **示例** ```objective-c AT+CFUN? +CFUN: 1 OK ``` 上述示例表示查询当前的CFUN模式,当前模式设置为1。 - **特点** - 以?结尾; - 不修改配置; - 查询当前状态。 ### 设置命令 - **格式** ```bash AT+=[,[,[...]]] ``` - **作用** 设置用户可定义的参数值。 - **示例** ```bash AT+CFUN=1 ``` 上述示例表明设置CFUN模式为1。 - **特点** - 带=号; - 且包含配置参数; - 用于修改配置。 ### 执行命令 - **格式** ```bash AT+ ``` - **作用** 返回特定的参数信息或执行特定的操作。 - **示例** ```bash AT+CIMI ``` 上述示例表明执行AT+CIMI(用于查询IMSI号)。 - **特点** 不带参数; 直接执行操作。 ## 添加自定义AT命令 需要使用到的接口文件: *qos_components\system\at\qosa_at_cmd.h* 增加自定义AT命令的基本流程如下: 1. 注册自定义AT命令: 系统在开机后的初始化阶段,通过 ***qosa_at_parser_add_cust_at()*** 添加自定义AT命令。该函数可以接收一个包含多个AT命令及其处理函数的表格。 2. 实现处理函数: 在AT处理函数中, 实现AT命令的功能。 以下通过一个最简单的AT处理函数,介绍如何实现自定义的AT处理函数。 该AT命令的名称为 **AT+QEXAMPLEHELLO**, 发送 **READ命令(AT+QEXAMPLEHELLO?)** 会返回"**+QEXAMPLEHELLO: Hello World!**"。 步骤一:注册自定义AT命令。 首先在开机初始化阶段调用 ***qosa_at_parser_add_cust_at()*** 注册AT命令 **+QEXAMPLEHELLO** 以及对应的处理函数。此处AT命令的字符串请勿加上AT字符。示例代码如下: ```c qosa_at_desc_t unir_examples_at_desc[] = { {"+QEXAMPLEHELLO", unir_exec_example_qexamplehello_cmd, 0}, {QOSA_NULL, QOSA_NULL, 0}}; qosa_at_parser_add_cust_at((const qosa_at_desc_t *)unir_examples_at_desc, QOSA_ARRAY_SIZE(unir_examples_at_desc)); ``` 步骤二:实现处理函数。 AT命令的处理并非简单的字符串匹配,而是一套高度抽象的命令分发机制。AT命令解析器框架已完成从物理链路层到应用逻辑层的解耦,因此开发者只需关注 *unir_exec_example_qexamplehello_cmd()* 这一回调函数,在该函数中完成自定义AT命令的业务逻辑。示例代码如下: ```cpp void unir_exec_example_qexamplehello_cmd(qosa_at_cmd_t *cmd) { char resp[128] = {0}; // Response string buffer switch (cmd->type) { case QOSA_AT_CMD_READ: { // Read command processing branch, e.g. input AT+QEXAMPLEHELLO? qosa_snprintf(resp, sizeof(resp), "%s", "+QEXAMPLEHELLO: Hello World!"); qosa_at_resp_cmd(cmd->dev_port, QOSA_ATCI_RESULT_CODE_OK, QOSA_CMD_RC_OK, resp, 1); } break; case QOSA_AT_CMD_SET: { // Set command processing branch, e.g. input AT+QEXAMPLEHELLO="cfg",10 // Get first string parameter test_str = (char *)qosa_at_param_string(cmd->params[0], ¶mok); if (!paramok) { // Return error when parameter is invalid qosa_at_resp_cme_error(cmd->dev_port, QOSA_ERR_AT_CME_PARAM_INVALID); return; } // Construct and return set command response qosa_snprintf(resp, sizeof(resp), "+QEXAMPLEHELLO: \"%s\"", test_str); qosa_at_resp_cmd(cmd->dev_port, QOSA_ATCI_RESULT_CODE_OK, QOSA_CMD_RC_OK, resp, 1); } break; default: // Unsupported command type, return operation not supported error QOSA_RETURN_CME_ERR(cmd->dev_port, QOSA_ERR_AT_CME_OPERATION_NOT_SUPPORTED); break; } } ``` 处理函数的参数功能说明如下: - cmd->type:用于确定命令的类型 - cmd->params:命令的参数集 - cmd->dev_port:命令传输通道, 后续回复AT命令需要向此通道传输 **命令分发** 当一条AT命令到达处理器时,SDK首先将其解析并封装进 *qosa_at_cmd_t* 结构体中。其中cmd->type是处理流程的核心字段,它定义了当前交互的语义: | **类型 (cmd->type)** | **对应AT指令示例** | **说明** | | --- | --- | --- | | *QOSA_AT_CMD_READ* | **AT+QEXAMPLEHELLO?** | **查询命令** | | *QOSA_AT_CMD_SET* | **AT+QEXAMPLEHELLO="cfg",1** | **设置命令** | | *QOSA_AT_CMD_TEST* | **AT+QEXAMPLEHELLO=?** | **测试命令** | | *QOSA_AT_CMD_EXE* | **AT+QEXAMPLEHELLO** | **执行命令** | **命令参数的解析与校验** SDK提供专用的API来解析cmd->params[]数组中的参数,并对解析结果进行校验。这些API具备自动类型转换和合法性检查功能。 - 解析API: 字符串解析API:*qosa_at_param_string(cmd->params[index], &ok)* 整型解析API:*qosa_at_param_int(cmd->params[index], &ok)* - 安全性校验:SDK引入了paramok机制,该变量可在解析过程中传递状态。解析API采用“逻辑短路”设计:一旦序列中某个参数解析失败,paramok立即被锁定为false状态,后续有效解析自动中止。开发者需在所有参数解析完成后,对paramok进行一次最终校验。 **响应回复机制** 发送命令执行结果时,必须关联cmd->dev_port,以确保响应能准确返回给发起请求的通道(如UART或USB)。 - 成功响应:使用 *qosa_at_resp_cmd()* 发送具体内容及OK状态。 - 错误响应:使用 *qosa_at_resp_cmd()*/*qosa_at_resp_cme_error()*/*QOSA_RETURN_CME_ERR* 返回标准错误码,便于上位机识别异常原因。 ## 自定义AT API ## 头文件 *qosa_at_cmd.h* ## 函数概览 | **函数** | **描述** | | --- | --- | | *qosa_at_parser_add_cust_at()* | 动态添加自定义AT命令 | | *qosa_at_resp_cmd()* | 返回结果并结束AT命令 | | *qosa_at_resp_cmd_info_text()* | 输出文本但不结束命令 | | *qosa_at_resp_finish()* | 结束AT命令并清理资源 | ## 函数详解 ### qosa_at_parser_add_cust_at - **功能描述** 动态添加自定义AT命令。用于应用层向系统注册自定义AT命令,用户可将命令描述符列表添加到AT解析器中,从而扩展模块的标准AT命令集。 - **函数原型** ```c void qosa_at_parser_add_cust_at(const qosa_at_desc_t *desc, qosa_uint32_t list_len) ``` - **参数说明** | **参数名** | **输入/输出** | **类型** | **说明** | | --- | --- | --- | --- | | *desc* | 输入 | const qosa_at_desc_t * | 指向自定义AT命令描述结构体列表的指针。有关自定义AT命令描述结构体详情,详见 [*qosa_at_desc_t*](#qosaatdesc_t) | | *list_len* | 输入 | qosa_uint32_t | 待添加的AT命令列表中的命令总数(总数需大于等于1) | - **返回值说明** 无 ```{note} 1. 该函数通常在应用初始化阶段调用。 2. 注册的命令名称不能与系统内置的标准AT命令重名。 ``` ### qosa_at_resp_cmd - **功能描述** 返回结果并结束AT命令。向指定的AT通道返回命令执行的最终结果,并正式结束当前AT命令的生命周期。支持返回 **OK**、**ERROR**、**CME ERROR** 及 **CMS ERROR** 等标准响应格式。 - **函数原型** ```c void qosa_at_resp_cmd(qosa_at_dev_type_e dev_port, qosa_atci_result_code_e resultCode, qosa_uint32_t report_code, char *rsp_buffer, qosa_uint8_t padding) ``` - **参数说明** | **参数名** | **输入/输出** | **类型** | **说明** | | --- | --- | --- | --- | | *dev_port* | 输入 | *qosa_at_dev_type_e* | AT命令的目标通道。详情请参考 [*qosa_at_dev_type_e*](#qosaatdevtypee) | | *resultCode* | 输入 | *qosa_atci_result_code_e* | 返回结果类型(如 *QOSA_ATCI_RESULT_CODE_OK*)。详情请参考 [*qosa_atci_result_code_e*](#qosaatciresultcodee) | | *report_code* | 输入 | qosa_uint32_t | 错误码。当结果为 **CME ERROR** 或 **CMS ERROR** 时,携带具体的错误编号。*report_code* 的取值取决于 *resultCode* 的类型。详情可参考 ***resultCode*****的使用说明** | | *rsp_buffer* | 输入 | char * | 响应内容缓冲区。仅在结果为 **OK** 且需要返回额外数据时有效。若无需返回数据,可设置为 *QOSA_NULL* | | *padding* | 输入 | qosa_uint8_t | 换行符(\r\n)填充模式配置,控制 *rsp_buffer* 内容前后是否自动添加换行。范围:0~4
  • *0*: 不添加
  • *1*: 自动在 *rsp_buffer* 前后增加换行符,如果 *rsp_buffer* 原始数据前后已有换行符, 则避免重复
  • *2*: 强制在 *rsp_buffe* r前后增加换行符
  • *3*: 强制在 *rsp_buffer* 前增加换行符
  • *4*: 强制在 *rsp_buffer* 后增加换行符
推荐使用 **1** | ***resultCode*的使用说明如下**: - 不推荐使用 *QOSA_ATCI_RESULT_CODE_NULL*。如需返回常规错误码, 请使用 *QOSA_ATCI_RESULT_CODE_ERROR*,并将 *report_code* 设定为 *QOSA_CMD_RC_ERROR*。示例代码如下: ```c qosa_at_resp_cmd(cmd->dev_port, QOSA_ATCI_RESULT_CODE_ERROR, QOSA_CMD_RC_ERROR, QOSA_NULL, 1) ``` - *QOSA_ATCI_RESULT_CODE_CMS_ERROR* 为短信相关的错误码, 不推荐使用。 - 当 *resultCode* 为 *QOSA_ATCI_RESULT_CODE_OK* 时, *report_cod* e应设为 *QOSA_CMD_RC_OK*。如果有字符串需要输出, *rsp_buffer* 应填入字符串地址;否则设为 *QOSA_NULL*。示例代码如下: ```c //无字符串输出 qosa_at_resp_cmd(cmd->dev_port, QOSA_ATCI_RESULT_CODE_OK, QOSA_CMD_RC_OK, QOSA_NULL, 1); //有字符串输出 qosa_at_resp_cmd(cmd->dev_port, QOSA_ATCI_RESULT_CODE_OK, QOSA_CMD_RC_OK, rsp_buffer, 1); ``` - 当 *resultCode* 为 *QOSA_ATCI_RESULT_CODE_CME_ERROR* 时, *report_code* 可填入 *QOSA_ERR_AT_CME_* 开头的CME相关错误码或者自定义错误码, *rsp_buffer* 需要保持为 *QOSA_NULL*。示例代码如下: ```c qosa_at_resp_cmd(cmd->dev_port, QOSA_ATCI_RESULT_CODE_CME_ERROR, QOSA_ERR_AT_CME_OPERATION_NOT_ALLOWED, QOSA_NULL, 1); ``` - **返回值说明** 无 ```{note} 1. 调用该函数后,当前AT命令处理流程即结束,通道重新进入待命状态。 2. 若仅需输出中间过程信息而不结束命令,请调用 ***qosa_at_resp_cmd_info_text()***。 ``` ### qosa_at_resp_cmd_info_text - **功能描述** 输出文本但不结束命令。在AT命令执行过程中输出文本信息,仅负责向指定通道发送数据,不会改变当前的AT解析状态,也不会结束正在执行的命令。 - **函数原型** ```c int qosa_at_resp_cmd_info_text(qosa_at_dev_type_e dev_port, const char *text, qosa_size_t length, unsigned char padding) ``` - **参数说明** | **参数名** | **输入/输出** | **类型** | **说明** | | --- | --- | --- | --- | | *dev_port* | 输入 | *qosa_at_dev_type_e* | AT命令的目标通道。详情请参考 [*qosa_at_dev_type_e*](#qosaatdevtypee) | | *text* | 输入 | const char * | 响应内容缓冲区。不可为空 | | *length* | 输入 | qosa_size_t | *text* 的长度。单位:字节。该长度需大于等于1 | | *padding* | 输入 | unsigned char | 换行符(\r\n)填充模式配置,控制 *text* 内容前后是否自动添加换行。范围:0~4
  • *0*: 不添加
  • *1*: 自动在 *text* 前后增加换行符, 如果 *text* 原始数据前后已有换行符, 则避免重复
  • *2*: 强制在 *text* 前后增加换行符
  • *3*: 强制在 *text* 前增加换行符
  • *4*: 强制在 *text* 后增加换行符
推荐使用 **1** | - **返回值说明** *0*:函数执行成功 *非0*:函数执行失败 ```{note} 1. 该函数常用于查询命令或需要分多行输出数据的场景。 2. 在使用 *qosa_at_resp_cmd_info_text()* 输出自定义响应内容后,必须调用 *qosa_at_resp_cmd()* 返回最终执行结果(如 **OK** 或 **ERROR**),以结束当前AT命令流程。 ``` ### qosa_at_resp_finish - **功能描述** 结束AT命令并清理资源。结束当前AT命令解析流程,清除当前状态,并向AT线程发送事件以继续读取新的输入数据。 - **函数原型** ```c int qosa_at_resp_finish(qosa_at_dev_type_e dev_port) ``` - **参数说明** | **参数名** | **输入/输出** | **类型** | **说明** | | --- | --- | --- | --- | | *dev_port* | 输入 | *qosa_at_dev_type_e* | AT命令的目标通道。详情请参考 [*qosa_at_dev_type_e*](#qosaatdevtypee) | - **返回值说明** *0*:函数执行成功 *非0*:函数执行失败 ```{note} 该函数用于AT命令处理的收尾操作,清除当前解析状态。调用后,AT线程将继续读取新的输入数据。 ``` ## 结构体定义 ### qosa_at_desc_t 自定义AT命令描述结构体定义如下: ```cpp typedef struct _qosa_at_desc { char *name; void (*handler)(struct _qosa_at_cmd *); qosa_uint32_t constrains; } qosa_at_desc_t; ``` | **参数** | **类型** | **说明** | | --- | --- | --- | | *name* | char * | 命令名称字符串。例如 *"***+IPR***"*、*"***+QEXAMPLEHELLO***"*。注册时只填写命令体,不包含前缀AT | | *handler* | void (*)(struct _qosa_at_cmd *) | 命令的处理回调。命令匹配成功后,AT解析器会调用此函数,并通过 [*qosa_at_cmd_t*](#qosaatcmd_t) 向回调传入通道、命令类型、参数列表等上下文信息 | | *constrains* | qosa_uint32_t | 保留参数(暂不使用,请保持为0) | **示例:** ```bash qosa_at_desc_t unir_examples_at_desc[] = { {"+qexampleedit", unir_exec_example_qexampleedit_cmd, 0}, {"+qexamplehello", unir_exec_example_qexamplehello_cmd, 0}, {"+qexampleraw", unir_exec_example_qexampleraw_cmd, 0}, {QOSA_NULL, QOSA_NULL, 0}}; ``` ### qosa_at_cmd_t *qosa_at_cmd_t* 是AT命令解析上下文结构体。命令匹配成功后,AT解析器构造该结构体,并通过 *qosa_at_desc_t* 中的 *handler* 传递给命令处理函数。处理函数通过该结构体判断命令类型、读取参数,并通过响应接口输出执行结果。 AT命令解析上下文结构体定义如下: ```cpp typedef struct _qosa_at_cmd { qosa_at_dev_type_e dev_port; qosa_uint8_t param_count; qosa_at_cmd_type_e type; qosa_at_desc_t *desc; qosa_bool_t resp_flag; qosa_bool_t err_flag; qosa_at_param_t *params[QOSA_AT_MAX_PARAM_CNT]; } qosa_at_cmd_t; ``` | **参数** | **类型** | **说明** | | --- | --- | --- | | *dev_port* | *qosa_at_dev_type_e* | AT命令的目标通道。详情请参考 [*qosa_at_dev_type_e*](#qosaatdevtypee) | | *param_count* | qosa_uint8_t | 当前命令解析得到的参数个数。业务处理时可结合 *params* 数组判断可访问的参数范围 | | *type* | qosa_at_cmd_type_e | 当前AT命令的执行模式,包括设置命令、测试命令、查询命令和执行命令。*handler* 可根据该字段区分处理逻辑 | | *desc* | qosa_at_desc_t * | 匹配到的AT命令注册描述信息,包含命令名称、处理回调和约束参数 | | *resp_flag* | qosa_bool_t | 响应输出控制标志,用于标记当前AT命令是否为首次输出,AT框架据此处理响应前的换行格式 | | *err_flag* | qosa_bool_t | 错误状态标志,用于记录当前AT命令执行过程中是否已经发生错误 | | *params* | qosa_at_param_t *[QOSA_AT_MAX_PARAM_CNT] | 参数数组,保存解析后的AT参数。业务处理函数通常配合 *qosa_at_param_\** 系列接口读取和校验参数 | ## 枚举定义 ### qosa_at_dev_type_e AT命令的目标通道枚举定义如下: ```c typedef enum { QOSA_DEV_NONE = -1, QOSA_DEV_SIOLIB_UART1_PORT, QOSA_DEV_SIOLIB_UART2_PORT, QOSA_DEV_SIOLIB_UART3_PORT, QOSA_DEV_SIOLIB_USB_AT_PORT, QOSA_DEV_SIOLIB_USB_MODEM, QOSA_DEV_SIOLIB_USB_AT2_PORT, QOSA_DEV_SIOLIB_USB_NMEA, QOSA_DEV_SIOLIB_DATA_MUX_0_PORT, QOSA_DEV_SIOLIB_DATA_MUX_1_PORT, QOSA_DEV_SIOLIB_DATA_MUX_2_PORT, QOSA_DEV_SIOLIB_DATA_MUX_3_PORT, QOSA_DEV_SIOLIB_DATA_MUX_4_PORT, QOSA_DEV_VIRT_AT_PORT, QOSA_DEV_PORT_MAX, } qosa_at_dev_type_e; ``` | **成员** | **说明** | | --- | --- | | *QOSA_DEV_NONE* | 无效端口或未指定端口,通常作为默认值或特殊场景占位值使用 | | *QOSA_DEV_SIOLIB_UART1_PORT* | UART1 AT通道,对应本地端口名 *uart1* | | *QOSA_DEV_SIOLIB_UART2_PORT* | UART2 AT通道,对应本地端口名 *uart2* | | *QOSA_DEV_SIOLIB_UART3_PORT* | UART3 AT通道,对应本地端口名 *uart3* | | *QOSA_DEV_SIOLIB_USB_AT_PORT* | 主USB AT通道,对应本地端口名 *usbat* | | *QOSA_DEV_SIOLIB_USB_MODEM* | USB Modem通道,对应调制解调器数据相关USB端口 | | *QOSA_DEV_SIOLIB_USB_AT2_PORT* | 第二路USB AT通道,用于多路USB AT访问场景 | | *QOSA_DEV_SIOLIB_USB_NMEA* | USB NMEA通道,对应本地端口名 *usbnmea* | | *QOSA_DEV_SIOLIB_DATA_MUX_0_PORT* | CMUX逻辑通道基准值,常用于与后续CMUX端口做偏移换算 | | *QOSA_DEV_SIOLIB_DATA_MUX_1_PORT* | 第1路CMUX AT/URC通道,对应本地端口名 *cmux1* | | *QOSA_DEV_SIOLIB_DATA_MUX_2_PORT* | 第2路CMUX AT/URC通道,对应本地端口名 *cmux2* | | *QOSA_DEV_SIOLIB_DATA_MUX_3_PORT* | 第3路CMUX AT/URC通道,对应本地端口名 *cmux3* | | *QOSA_DEV_SIOLIB_DATA_MUX_4_PORT* | 第4路CMUX AT/URC通道,对应本地端口名 *cmux4* | | *QOSA_DEV_VIRT_AT_PORT* | 虚拟AT通道,对应本地端口名 *virtat*,用于虚拟或内部转发场景 | | *QOSA_DEV_PORT_MAX* | 枚举边界值,表示端口类型上限,不作为实际AT端口使用 | ### qosa_atci_result_code_e 返回结果类型枚举定义如下: ```c typedef enum QOSA_ATCI_RESULT_CODE { QOSA_ATCI_RESULT_CODE_NULL, QOSA_ATCI_RESULT_CODE_OK, QOSA_ATCI_RESULT_CODE_ERROR, QOSA_ATCI_RESULT_CODE_CME_ERROR, QOSA_ATCI_RESULT_CODE_CMS_ERROR, QOSA_ATCI_RESULT_CODE_MAX } qosa_atci_result_code_e; ``` | **成员** | **说明** | | --- | --- | | *QOSA_ATCI_RESULT_CODE_NULL* | 空结果类型。一般不推荐使用,实际开发中应优先使用更明确的 **OK**、**ERROR**、**CME ERROR** 或 **CMS ERROR** | | *QOSA_ATCI_RESULT_CODE_OK* | 命令执行成功。若同时传入 *rsp_buffer*,会先输出内容,再输出最终 **OK** | | *QOSA_ATCI_RESULT_CODE_ERROR* | 返回标准 **ERROR** 结果,适用于通用失败场景 | | *QOSA_ATCI_RESULT_CODE_CME_ERROR* | 返回 **+CME ERROR: ** 类型错误,适用于设备、参数、网络等ME相关错误 | | *QOSA_ATCI_RESULT_CODE_CMS_ERROR* | 返回 **+CMS ERROR: ** 类型错误,适用于短信业务相关失败场景 | | *QOSA_ATCI_RESULT_CODE_MAX* | 枚举边界值,用于范围控制,不作为实际返回结果使用 | # 应用逻辑流程图 该流程在系统启动的初始化阶段执行,目的是注册自定义AT命令:当接收到该命令字符串时,系统将调用对应的处理函数。 ```{figure} images/board_HEdJwxKlYhfxwRbMG0UclyCpncc.jpg :align: center :alt: image ``` 用户通过串口发送命令(如 **AT+QEXAMPLEHELLO?**)后,AT命令解析器框架将解析该命令并触发相应的回调函数。 ```{figure} images/board_C7hgw4jRfhhkbvb6Ny8cZcCtnQe.jpg :align: center :alt: image ``` # 示例代码 完整示例代码请查看 https://github.com/UniRTOS/UniRTOS-Doc-Examples/blob/main/system/at/at_demo.c 。