From c7835a1e4bc38b1964d823fde5867b443909e82a Mon Sep 17 00:00:00 2001 From: fengzero Date: Fri, 15 Aug 2025 08:50:23 +0000 Subject: [PATCH] comments --- plugins/modbus/modbus.c | 110 +++- plugins/modbus/modbus.h | 200 ++++-- plugins/modbus/modbus_point.c | 1053 ++++++++++++++++-------------- plugins/modbus/modbus_point.h | 126 +++- plugins/modbus/modbus_req.c | 44 +- plugins/modbus/modbus_req.h | 127 +++- plugins/modbus/modbus_rtu.c | 102 ++- plugins/modbus/modbus_stack.c | 87 ++- plugins/modbus/modbus_stack.h | 96 ++- plugins/modbus/modbus_tcp.c | 110 +++- plugins/monitor/monitor.c | 237 ++++++- plugins/monitor/monitor_config.c | 56 +- plugins/monitor/mqtt_handle.c | 143 +++- plugins/mqtt/mqtt_config.c | 223 +++++-- plugins/mqtt/mqtt_handle.c | 271 ++++++-- plugins/mqtt/mqtt_plugin.c | 245 +++++-- src/base/group.c | 291 +++++++-- src/base/metrics.c | 189 +++++- src/base/tag.c | 130 +++- src/base/tag_sort.c | 73 ++- src/base/template.c | 183 +++++- src/connection/connection.c | 236 ++++++- src/connection/mqtt_client.c | 442 ++++++++++--- src/core/manager.c | 185 +++++- src/core/manager_internal.c | 328 +++++++++- src/core/node_manager.c | 281 +++++++- src/core/plugin_manager.c | 201 +++++- src/core/storage.c | 346 +++++++++- src/core/subscribe.c | 270 +++++++- src/core/template_manager.c | 151 ++++- src/event/event_linux.c | 229 +++++-- 31 files changed, 5535 insertions(+), 1230 deletions(-) diff --git a/plugins/modbus/modbus.c b/plugins/modbus/modbus.c index fbf16bb62..cf4275ab6 100644 --- a/plugins/modbus/modbus.c +++ b/plugins/modbus/modbus.c @@ -23,6 +23,22 @@ #include "modbus.h" +/** + * modbus.c + * 本文件实现了 Modbus 协议的基础功能,包括: + * - 数据包的打包和解包 + * - CRC 校验计算 + * - 字节序转换 + * - 辅助功能 + */ + +/** + * 计算 Modbus RTU 的 CRC 校验值 + * + * @param buf 数据缓冲区 + * @param len 数据长度 + * @return CRC 校验值 + */ static uint16_t calcrc(uint8_t *buf, int len) { uint16_t crc = 0xffff; @@ -42,6 +58,12 @@ static uint16_t calcrc(uint8_t *buf, int len) return crc; } +/** + * 打包 Modbus 报文头部 + * + * @param buf 打包缓冲区 + * @param seq 序列号 + */ void modbus_header_wrap(neu_protocol_pack_buf_t *buf, uint16_t seq) { assert(neu_protocol_pack_buf_unused_size(buf) >= @@ -53,11 +75,18 @@ void modbus_header_wrap(neu_protocol_pack_buf_t *buf, uint16_t seq) header->seq = htons(seq); header->protocol = 0x0; header->len = htons(neu_protocol_pack_buf_used_size(buf) - - sizeof(struct modbus_header)); + sizeof(struct modbus_header)); } +/** + * 解析 Modbus 报文头部 + * + * @param buf 解包缓冲区 + * @param out_header 输出头部结构 + * @return 成功返回头部大小,失败返回负数 + */ int modbus_header_unwrap(neu_protocol_unpack_buf_t *buf, - struct modbus_header * out_header) + struct modbus_header *out_header) { struct modbus_header *header = (struct modbus_header *) neu_protocol_unpack_buf( @@ -79,6 +108,13 @@ int modbus_header_unwrap(neu_protocol_unpack_buf_t *buf, return sizeof(struct modbus_header); } +/** + * 打包 Modbus 功能码 + * + * @param buf 打包缓冲区 + * @param slave_id 从站 ID + * @param function 功能码 + */ void modbus_code_wrap(neu_protocol_pack_buf_t *buf, uint8_t slave_id, uint8_t function) { @@ -91,8 +127,15 @@ void modbus_code_wrap(neu_protocol_pack_buf_t *buf, uint8_t slave_id, code->function = function; } +/** + * 解析 Modbus 功能码 + * + * @param buf 解包缓冲区 + * @param out_code 输出功能码结构 + * @return 成功返回功能码大小,失败返回负数 + */ int modbus_code_unwrap(neu_protocol_unpack_buf_t *buf, - struct modbus_code * out_code) + struct modbus_code *out_code) { struct modbus_code *code = (struct modbus_code *) neu_protocol_unpack_buf( buf, sizeof(struct modbus_code)); @@ -106,6 +149,14 @@ int modbus_code_unwrap(neu_protocol_unpack_buf_t *buf, return sizeof(struct modbus_code); } +/** + * 打包 Modbus 地址 + * + * @param buf 打包缓冲区 + * @param start 起始地址 + * @param n_register 寄存器数量 + * @param action Modbus 动作类型 + */ void modbus_address_wrap(neu_protocol_pack_buf_t *buf, uint16_t start, uint16_t n_register, enum modbus_action action) { @@ -127,8 +178,15 @@ void modbus_address_wrap(neu_protocol_pack_buf_t *buf, uint16_t start, } } +/** + * 解析 Modbus 地址 + * + * @param buf 解包缓冲区 + * @param out_address 输出地址结构 + * @return 成功返回地址大小,失败返回负数 + */ int modbus_address_unwrap(neu_protocol_unpack_buf_t *buf, - struct modbus_address * out_address) + struct modbus_address *out_address) { struct modbus_address *address = (struct modbus_address *) neu_protocol_unpack_buf( @@ -145,6 +203,14 @@ int modbus_address_unwrap(neu_protocol_unpack_buf_t *buf, return sizeof(struct modbus_address); } +/** + * 打包 Modbus 数据 + * + * @param buf 打包缓冲区 + * @param n_byte 数据字节数 + * @param bytes 数据字节数组 + * @param action Modbus 动作类型 + */ void modbus_data_wrap(neu_protocol_pack_buf_t *buf, uint8_t n_byte, uint8_t *bytes, enum modbus_action action) { @@ -163,8 +229,15 @@ void modbus_data_wrap(neu_protocol_pack_buf_t *buf, uint8_t n_byte, } } +/** + * 解析 Modbus 数据 + * + * @param buf 解包缓冲区 + * @param out_data 输出数据结构 + * @return 成功返回数据头大小,失败返回负数 + */ int modbus_data_unwrap(neu_protocol_unpack_buf_t *buf, - struct modbus_data * out_data) + struct modbus_data *out_data) { struct modbus_data *mdata = (struct modbus_data *) neu_protocol_unpack_buf( buf, sizeof(struct modbus_data)); @@ -179,16 +252,26 @@ int modbus_data_unwrap(neu_protocol_unpack_buf_t *buf, return sizeof(struct modbus_data); } +/** + * 计算并设置 Modbus CRC 校验值 + * + * @param buf 打包缓冲区 + */ void modbus_crc_set(neu_protocol_pack_buf_t *buf) { uint16_t crc = calcrc(neu_protocol_pack_buf_get(buf), - neu_protocol_pack_buf_used_size(buf) - 2); + neu_protocol_pack_buf_used_size(buf) - 2); uint16_t *p_crc = (uint16_t *) neu_protocol_pack_buf_set( buf, neu_protocol_pack_buf_used_size(buf) - 2, 2); *p_crc = crc; } +/** + * 在缓冲区中预留 CRC 校验的空间 + * + * @param buf 打包缓冲区 + */ void modbus_crc_wrap(neu_protocol_pack_buf_t *buf) { assert(neu_protocol_pack_buf_unused_size(buf) >= sizeof(struct modbus_crc)); @@ -198,8 +281,15 @@ void modbus_crc_wrap(neu_protocol_pack_buf_t *buf) crc->crc = 0; } +/** + * 解析 Modbus CRC 校验 + * + * @param buf 解包缓冲区 + * @param out_crc 输出 CRC 结构 + * @return 成功返回 CRC 大小,失败返回负数 + */ int modbus_crc_unwrap(neu_protocol_unpack_buf_t *buf, - struct modbus_crc * out_crc) + struct modbus_crc *out_crc) { struct modbus_crc *crc = (struct modbus_crc *) neu_protocol_unpack_buf( buf, sizeof(struct modbus_crc)); @@ -213,6 +303,12 @@ int modbus_crc_unwrap(neu_protocol_unpack_buf_t *buf, return sizeof(struct modbus_crc); } +/** + * 将 Modbus 区域类型转换为字符串 + * + * @param area 区域类型 + * @return 区域类型的字符串表示 + */ const char *modbus_area_to_str(modbus_area_e area) { switch (area) { diff --git a/plugins/modbus/modbus.h b/plugins/modbus/modbus.h index dc7c957af..0908d9919 100644 --- a/plugins/modbus/modbus.h +++ b/plugins/modbus/modbus.h @@ -19,109 +19,221 @@ #ifndef _NEU_M_PLUGIN_MODBUS_H_ #define _NEU_M_PLUGIN_MODBUS_H_ +/** + * modbus.h + * 本文件定义了 Modbus 协议的基础数据结构和常量,包括: + * - 功能码枚举 + * - 区域类型枚举 + * - 字节序枚举 + * - 协议报文结构 + * - 数据包处理函数 + */ + #include #include #include -/* LL -> LE 1,2,3,4 - LB -> LE 2,1,4,3 - BB -> BE 3,4,1,2 - BL -> BE 4,3,2,1 - L64 -> 1,2,3,4,5,6,7,8 - B64 -> 8,7,6,5,4,3,2,1 +/* 字节序说明: + LL -> LE 1,2,3,4 (小端,小端) + LB -> LE 2,1,4,3 (小端,大端) + BB -> BE 3,4,1,2 (大端,大端) + BL -> BE 4,3,2,1 (大端,小端) + L64 -> 1,2,3,4,5,6,7,8 (小端) + B64 -> 8,7,6,5,4,3,2,1 (大端) */ -/* big-endian order */ +/* 大端字节序 */ +/** + * Modbus 动作类型枚举 + */ typedef enum modbus_action { - MODBUS_ACTION_DEFAULT = 0, - MODBUS_ACTION_HOLD_REG_WRITE = 1 + MODBUS_ACTION_DEFAULT = 0, // 默认动作 + MODBUS_ACTION_HOLD_REG_WRITE = 1 // 保持寄存器写入动作 } modbus_action_e; +/** + * Modbus 功能码枚举,定义了支持的所有功能码和错误码 + */ typedef enum modbus_function { - MODBUS_READ_COIL = 0x1, - MODBUS_READ_INPUT = 0x02, - MODBUS_READ_HOLD_REG = 0x03, - MODBUS_READ_INPUT_REG = 0x04, - MODBUS_WRITE_S_COIL = 0x05, - MODBUS_WRITE_S_HOLD_REG = 0x06, - MODBUS_WRITE_M_HOLD_REG = 0x10, - MODBUS_WRITE_M_COIL = 0x0F, - MODBUS_READ_COIL_ERR = 0x81, - MODBUS_READ_INPUT_ERR = 0x82, - MODBUS_READ_HOLD_REG_ERR = 0x83, - MODBUS_READ_INPUT_REG_ERR = 0x84, - MODBUS_WRITE_S_COIL_ERR = 0x85, - MODBUS_WRITE_S_HOLD_REG_ERR = 0x86, - MODBUS_WRITE_M_HOLD_REG_ERR = 0x90, - MODBUS_WRITE_M_COIL_ERR = 0x8F, - MODBUS_DEVICE_ERR = -2 + MODBUS_READ_COIL = 0x1, // 读线圈 + MODBUS_READ_INPUT = 0x02, // 读离散输入 + MODBUS_READ_HOLD_REG = 0x03, // 读保持寄存器 + MODBUS_READ_INPUT_REG = 0x04, // 读输入寄存器 + MODBUS_WRITE_S_COIL = 0x05, // 写单个线圈 + MODBUS_WRITE_S_HOLD_REG = 0x06, // 写单个保持寄存器 + MODBUS_WRITE_M_HOLD_REG = 0x10, // 写多个保持寄存器 + MODBUS_WRITE_M_COIL = 0x0F, // 写多个线圈 + MODBUS_READ_COIL_ERR = 0x81, // 读线圈错误 + MODBUS_READ_INPUT_ERR = 0x82, // 读离散输入错误 + MODBUS_READ_HOLD_REG_ERR = 0x83, // 读保持寄存器错误 + MODBUS_READ_INPUT_REG_ERR = 0x84, // 读输入寄存器错误 + MODBUS_WRITE_S_COIL_ERR = 0x85, // 写单个线圈错误 + MODBUS_WRITE_S_HOLD_REG_ERR = 0x86, // 写单个保持寄存器错误 + MODBUS_WRITE_M_HOLD_REG_ERR = 0x90, // 写多个保持寄存器错误 + MODBUS_WRITE_M_COIL_ERR = 0x8F, // 写多个线圈错误 + MODBUS_DEVICE_ERR = -2 // 设备错误 } modbus_function_e; +/** + * Modbus 区域类型枚举,定义了 Modbus 协议的四种数据区域 + */ typedef enum modbus_area { - MODBUS_AREA_COIL = 0, - MODBUS_AREA_INPUT = 1, - MODBUS_AREA_INPUT_REGISTER = 3, - MODBUS_AREA_HOLD_REGISTER = 4, + MODBUS_AREA_COIL = 0, // 线圈区域(可读写的位区域) + MODBUS_AREA_INPUT = 1, // 离散输入区域(只读的位区域) + MODBUS_AREA_INPUT_REGISTER = 3, // 输入寄存器区域(只读的寄存器区域) + MODBUS_AREA_HOLD_REGISTER = 4, // 保持寄存器区域(可读写的寄存器区域) } modbus_area_e; +/** + * Modbus 64位数据字节序枚举 + */ typedef enum modbus_endianess_64 { - MODBUS_LL = 1, // 12 34 56 78 - MODBUS_LB = 2, // 21 43 65 87 - MODBUS_BB = 3, // 87 65 43 21 - MODBUS_BL = 4, // 78 56 34 12 + MODBUS_LL = 1, // 小端-小端:12 34 56 78 + MODBUS_LB = 2, // 小端-大端:21 43 65 87 + MODBUS_BB = 3, // 大端-大端:87 65 43 21 + MODBUS_BL = 4, // 大端-小端:78 56 34 12 } modbus_endianess_64; +/** + * Modbus TCP 协议报文头部结构 + */ struct modbus_header { - uint16_t seq; - uint16_t protocol; - uint16_t len; + uint16_t seq; // 序列号 + uint16_t protocol; // 协议标识 + uint16_t len; // 数据长度 } __attribute__((packed)); +/** + * 打包 Modbus 报文头部 + * @param buf 打包缓冲区 + * @param seq 序列号 + */ void modbus_header_wrap(neu_protocol_pack_buf_t *buf, uint16_t seq); + +/** + * 解析 Modbus 报文头部 + * @param buf 解包缓冲区 + * @param out_header 输出头部结构 + * @return 成功返回头部大小,失败返回负数 + */ int modbus_header_unwrap(neu_protocol_unpack_buf_t *buf, struct modbus_header * out_header); +/** + * Modbus 功能码结构 + */ struct modbus_code { - uint8_t slave_id; - uint8_t function; + uint8_t slave_id; // 从站 ID + uint8_t function; // 功能码 } __attribute__((packed)); +/** + * 打包 Modbus 功能码 + * @param buf 打包缓冲区 + * @param slave_id 从站 ID + * @param function 功能码 + */ void modbus_code_wrap(neu_protocol_pack_buf_t *buf, uint8_t slave_id, uint8_t function); + +/** + * 解析 Modbus 功能码 + * @param buf 解包缓冲区 + * @param out_code 输出功能码结构 + * @return 成功返回功能码大小,失败返回负数 + */ int modbus_code_unwrap(neu_protocol_unpack_buf_t *buf, struct modbus_code * out_code); +/** + * Modbus 地址结构 + */ struct modbus_address { - uint16_t start_address; - uint16_t n_reg; + uint16_t start_address; // 起始地址 + uint16_t n_reg; // 寄存器数量 } __attribute__((packed)); +/** + * 打包 Modbus 地址 + * @param buf 打包缓冲区 + * @param start 起始地址 + * @param n_register 寄存器数量 + * @param m_action Modbus 动作类型 + */ void modbus_address_wrap(neu_protocol_pack_buf_t *buf, uint16_t start, uint16_t n_register, enum modbus_action m_action); + +/** + * 解析 Modbus 地址 + * @param buf 解包缓冲区 + * @param out_address 输出地址结构 + * @return 成功返回地址大小,失败返回负数 + */ int modbus_address_unwrap(neu_protocol_unpack_buf_t *buf, struct modbus_address * out_address); +/** + * Modbus 数据结构 + */ struct modbus_data { - uint8_t n_byte; - uint8_t byte[]; + uint8_t n_byte; // 数据字节数 + uint8_t byte[]; // 数据字节数组(可变长度) } __attribute__((packed)); +/** + * 打包 Modbus 数据 + * @param buf 打包缓冲区 + * @param n_byte 数据字节数 + * @param bytes 数据字节数组 + * @param action Modbus 动作类型 + */ void modbus_data_wrap(neu_protocol_pack_buf_t *buf, uint8_t n_byte, uint8_t *bytes, enum modbus_action action); + +/** + * 解析 Modbus 数据 + * @param buf 解包缓冲区 + * @param out_data 输出数据结构 + * @return 成功返回数据头大小,失败返回负数 + */ int modbus_data_unwrap(neu_protocol_unpack_buf_t *buf, struct modbus_data * out_data); +/** + * Modbus RTU CRC 校验结构 + */ struct modbus_crc { - uint16_t crc; + uint16_t crc; // CRC 校验值 } __attribute__((packed)); +/** + * 计算并设置 Modbus CRC 校验值 + * @param buf 打包缓冲区 + */ void modbus_crc_set(neu_protocol_pack_buf_t *buf); + +/** + * 在缓冲区中预留 CRC 校验的空间 + * @param buf 打包缓冲区 + */ void modbus_crc_wrap(neu_protocol_pack_buf_t *buf); + +/** + * 解析 Modbus CRC 校验 + * @param buf 解包缓冲区 + * @param out_crc 输出 CRC 结构 + * @return 成功返回 CRC 大小,失败返回负数 + */ int modbus_crc_unwrap(neu_protocol_unpack_buf_t *buf, struct modbus_crc * out_crc); +/** + * 将 Modbus 区域类型转换为字符串 + * @param area 区域类型 + * @return 区域类型的字符串表示 + */ const char *modbus_area_to_str(modbus_area_e area); #endif \ No newline at end of file diff --git a/plugins/modbus/modbus_point.c b/plugins/modbus/modbus_point.c index a58442669..5490059a9 100644 --- a/plugins/modbus/modbus_point.c +++ b/plugins/modbus/modbus_point.c @@ -9,535 +9,616 @@ * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See/** + * 转换64位数据的字节序 + * + * @param value 数据值,会被修改 + * @param endianess_64 目标字节序 + */ +void modbus_convert_endianess_64(neu_value_u *value, + modbus_endianess_64 endianess_64) +{ + switch (endianess_64) { GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - **/ + * along with this program; + if not + , write to the Free Software Foundation, *Inc., 51 Franklin Street, + Fifth Floor, Boston, MA 02110 - 1301, + USA.** / #include #include #include "modbus_point.h" -struct modbus_sort_ctx { - uint16_t start; - uint16_t end; -}; + /** + * modbus_point.c + * 本文件实现了 Modbus 点(数据点)相关的功能,包括: + * - 将通用数据标签转换为 Modbus 协议点 + * - 处理点的读写操作 + * - 对点进行排序与分组 + * - 处理不同数据类型的字节序转换 + */ + + struct modbus_sort_ctx { + uint16_t start; // 起始地址 + uint16_t end; // 结束地址 + }; + + // 单次 Modbus 读操作最大字节数 + static __thread uint16_t modbus_read_max_byte = 250; + + // 用于排序的比较函数(读操作) + static int tag_cmp(neu_tag_sort_elem_t * tag1, + neu_tag_sort_elem_t * tag2); + // 判断标签是否可以放入同一组(读操作) + static bool tag_sort(neu_tag_sort_t * sort, void *tag, + void *tag_to_be_sorted); + // 用于排序的比较函数(写操作) + static int tag_cmp_write(neu_tag_sort_elem_t * tag1, + neu_tag_sort_elem_t * tag2); + // 判断标签是否可以放入同一组(写操作) + static bool tag_sort_write(neu_tag_sort_t * sort, void *tag, + void *tag_to_be_sorted); + + /** + * 将通用数据标签转换为 Modbus 点 + * + * @param tag 数据标签 + * @param point 输出 Modbus 点 + * @return 成功返回 NEU_ERR_SUCCESS,失败返回错误码 + */ + int modbus_tag_to_point(const neu_datatag_t *tag, modbus_point_t *point) + { + int ret = NEU_ERR_SUCCESS; + uint32_t start_address = 0; + // 解析地址选项 + ret = neu_datatag_parse_addr_option(tag, &point->option); + if (ret != 0) { + return NEU_ERR_TAG_ADDRESS_FORMAT_INVALID; + } -static __thread uint16_t modbus_read_max_byte = 250; + char area = 0; + int n = sscanf(tag->address, "%hhu!%c%u", &point->slave_id, &area, + &start_address); + if (n != 3 || start_address == 0) { + return NEU_ERR_TAG_ADDRESS_FORMAT_INVALID; + } -static int tag_cmp(neu_tag_sort_elem_t *tag1, neu_tag_sort_elem_t *tag2); -static bool tag_sort(neu_tag_sort_t *sort, void *tag, void *tag_to_be_sorted); -static int tag_cmp_write(neu_tag_sort_elem_t *tag1, neu_tag_sort_elem_t *tag2); -static bool tag_sort_write(neu_tag_sort_t *sort, void *tag, - void *tag_to_be_sorted); + point->start_address = (uint16_t) start_address; -int modbus_tag_to_point(const neu_datatag_t *tag, modbus_point_t *point) -{ - int ret = NEU_ERR_SUCCESS; - uint32_t start_address = 0; - ret = neu_datatag_parse_addr_option(tag, &point->option); - if (ret != 0) { - return NEU_ERR_TAG_ADDRESS_FORMAT_INVALID; - } - - char area = 0; - int n = sscanf(tag->address, "%hhu!%c%u", &point->slave_id, &area, - &start_address); - if (n != 3 || start_address == 0) { - return NEU_ERR_TAG_ADDRESS_FORMAT_INVALID; - } - - point->start_address = (uint16_t) start_address; - - point->start_address -= 1; - point->type = tag->type; - - switch (area) { - case '0': - point->area = MODBUS_AREA_COIL; - break; - case '1': - point->area = MODBUS_AREA_INPUT; - break; - case '3': - point->area = MODBUS_AREA_INPUT_REGISTER; - break; - case '4': - point->area = MODBUS_AREA_HOLD_REGISTER; - break; - default: - return NEU_ERR_TAG_ADDRESS_FORMAT_INVALID; - } - - if (point->area == MODBUS_AREA_INPUT || - point->area == MODBUS_AREA_INPUT_REGISTER) { - if ((tag->attribute & NEU_ATTRIBUTE_WRITE) == NEU_ATTRIBUTE_WRITE) { - return NEU_ERR_TAG_ATTRIBUTE_NOT_SUPPORT; - } - } + point->start_address -= 1; + point->type = tag->type; - switch (point->area) { - case MODBUS_AREA_INPUT: - case MODBUS_AREA_COIL: - if (point->type != NEU_TYPE_BIT) { - return NEU_ERR_TAG_TYPE_NOT_SUPPORT; - } - if (point->option.bit.bit > 7) { - return NEU_ERR_TAG_ADDRESS_FORMAT_INVALID; - } - break; - case MODBUS_AREA_INPUT_REGISTER: - case MODBUS_AREA_HOLD_REGISTER: - if (point->type == NEU_TYPE_STRING && - point->option.string.length <= 0) { - return NEU_ERR_TAG_ADDRESS_FORMAT_INVALID; - } - if (point->type == NEU_TYPE_BYTES && point->option.bytes.length <= 0) { - return NEU_ERR_TAG_ADDRESS_FORMAT_INVALID; - } - if (point->type == NEU_TYPE_BIT && point->option.bit.bit > 15) { - return NEU_ERR_TAG_ADDRESS_FORMAT_INVALID; - } - if (point->type == NEU_TYPE_BIT && - (tag->attribute & NEU_ATTRIBUTE_WRITE) == NEU_ATTRIBUTE_WRITE) { - return NEU_ERR_TAG_ATTRIBUTE_NOT_SUPPORT; - } - break; - } - - switch (point->type) { - case NEU_TYPE_BIT: - point->n_register = 1; - break; - case NEU_TYPE_UINT16: - case NEU_TYPE_INT16: - if (point->area == MODBUS_AREA_COIL || - point->area == MODBUS_AREA_INPUT) { - ret = NEU_ERR_TAG_TYPE_NOT_SUPPORT; - } else { - point->n_register = 1; - } - break; - case NEU_TYPE_UINT32: - case NEU_TYPE_INT32: - case NEU_TYPE_FLOAT: - if (point->area == MODBUS_AREA_COIL || - point->area == MODBUS_AREA_INPUT) { - ret = NEU_ERR_TAG_TYPE_NOT_SUPPORT; - } else { - point->n_register = 2; - } - break; - case NEU_TYPE_UINT64: - case NEU_TYPE_INT64: - case NEU_TYPE_DOUBLE: - if (point->area == MODBUS_AREA_COIL || - point->area == MODBUS_AREA_INPUT) { - ret = NEU_ERR_TAG_TYPE_NOT_SUPPORT; - } else { - point->n_register = 4; - } - break; - case NEU_TYPE_STRING: - if (point->area == MODBUS_AREA_COIL || - point->area == MODBUS_AREA_INPUT) { - ret = NEU_ERR_TAG_TYPE_NOT_SUPPORT; - } else { - if (point->option.string.length > 127) { + switch (area) { + case '0': + point->area = MODBUS_AREA_COIL; + break; + case '1': + point->area = MODBUS_AREA_INPUT; + break; + case '3': + point->area = MODBUS_AREA_INPUT_REGISTER; + break; + case '4': + point->area = MODBUS_AREA_HOLD_REGISTER; + break; + default: return NEU_ERR_TAG_ADDRESS_FORMAT_INVALID; } - switch (point->option.string.type) { - case NEU_DATATAG_STRING_TYPE_H: - case NEU_DATATAG_STRING_TYPE_L: - point->n_register = point->option.string.length / 2 + - point->option.string.length % 2; + + if (point->area == MODBUS_AREA_INPUT || + point->area == MODBUS_AREA_INPUT_REGISTER) { + if ((tag->attribute & NEU_ATTRIBUTE_WRITE) == + NEU_ATTRIBUTE_WRITE) { + return NEU_ERR_TAG_ATTRIBUTE_NOT_SUPPORT; + } + } + + switch (point->area) { + case MODBUS_AREA_INPUT: + case MODBUS_AREA_COIL: + if (point->type != NEU_TYPE_BIT) { + return NEU_ERR_TAG_TYPE_NOT_SUPPORT; + } + if (point->option.bit.bit > 7) { + return NEU_ERR_TAG_ADDRESS_FORMAT_INVALID; + } break; - case NEU_DATATAG_STRING_TYPE_D: - case NEU_DATATAG_STRING_TYPE_E: - point->n_register = point->option.string.length; + case MODBUS_AREA_INPUT_REGISTER: + case MODBUS_AREA_HOLD_REGISTER: + if (point->type == NEU_TYPE_STRING && + point->option.string.length <= 0) { + return NEU_ERR_TAG_ADDRESS_FORMAT_INVALID; + } + if (point->type == NEU_TYPE_BYTES && + point->option.bytes.length <= 0) { + return NEU_ERR_TAG_ADDRESS_FORMAT_INVALID; + } + if (point->type == NEU_TYPE_BIT && point->option.bit.bit > 15) { + return NEU_ERR_TAG_ADDRESS_FORMAT_INVALID; + } + if (point->type == NEU_TYPE_BIT && + (tag->attribute & NEU_ATTRIBUTE_WRITE) == + NEU_ATTRIBUTE_WRITE) { + return NEU_ERR_TAG_ATTRIBUTE_NOT_SUPPORT; + } break; } - } - break; - case NEU_TYPE_BYTES: - if (point->area == MODBUS_AREA_COIL || - point->area == MODBUS_AREA_INPUT) { - ret = NEU_ERR_TAG_TYPE_NOT_SUPPORT; - } else { - if (point->option.bytes.length > 128 || - point->option.bytes.length % 2 == 1) { - return NEU_ERR_TAG_ADDRESS_FORMAT_INVALID; + + switch (point->type) { + case NEU_TYPE_BIT: + point->n_register = 1; + break; + case NEU_TYPE_UINT16: + case NEU_TYPE_INT16: + if (point->area == MODBUS_AREA_COIL || + point->area == MODBUS_AREA_INPUT) { + ret = NEU_ERR_TAG_TYPE_NOT_SUPPORT; + } else { + point->n_register = 1; + } + break; + case NEU_TYPE_UINT32: + case NEU_TYPE_INT32: + case NEU_TYPE_FLOAT: + if (point->area == MODBUS_AREA_COIL || + point->area == MODBUS_AREA_INPUT) { + ret = NEU_ERR_TAG_TYPE_NOT_SUPPORT; + } else { + point->n_register = 2; + } + break; + case NEU_TYPE_UINT64: + case NEU_TYPE_INT64: + case NEU_TYPE_DOUBLE: + if (point->area == MODBUS_AREA_COIL || + point->area == MODBUS_AREA_INPUT) { + ret = NEU_ERR_TAG_TYPE_NOT_SUPPORT; + } else { + point->n_register = 4; + } + break; + case NEU_TYPE_STRING: + if (point->area == MODBUS_AREA_COIL || + point->area == MODBUS_AREA_INPUT) { + ret = NEU_ERR_TAG_TYPE_NOT_SUPPORT; + } else { + if (point->option.string.length > 127) { + return NEU_ERR_TAG_ADDRESS_FORMAT_INVALID; + } + switch (point->option.string.type) { + case NEU_DATATAG_STRING_TYPE_H: + case NEU_DATATAG_STRING_TYPE_L: + point->n_register = point->option.string.length / 2 + + point->option.string.length % 2; + break; + case NEU_DATATAG_STRING_TYPE_D: + case NEU_DATATAG_STRING_TYPE_E: + point->n_register = point->option.string.length; + break; + } + } + break; + case NEU_TYPE_BYTES: + if (point->area == MODBUS_AREA_COIL || + point->area == MODBUS_AREA_INPUT) { + ret = NEU_ERR_TAG_TYPE_NOT_SUPPORT; + } else { + if (point->option.bytes.length > 128 || + point->option.bytes.length % 2 == 1) { + return NEU_ERR_TAG_ADDRESS_FORMAT_INVALID; + } + point->n_register = point->option.bytes.length / 2 + + point->option.bytes.length % 2; + } + break; + default: + return NEU_ERR_TAG_TYPE_NOT_SUPPORT; } - point->n_register = - point->option.bytes.length / 2 + point->option.bytes.length % 2; + + strncpy(point->name, tag->name, sizeof(point->name)); + return ret; } - break; - default: - return NEU_ERR_TAG_TYPE_NOT_SUPPORT; - } - strncpy(point->name, tag->name, sizeof(point->name)); - return ret; -} + int modbus_write_tag_to_point(const neu_plugin_tag_value_t *tag, + modbus_point_write_t *point) + { + int ret = NEU_ERR_SUCCESS; + ret = modbus_tag_to_point(tag->tag, &point->point); + point->value = tag->value; + return ret; + } -int modbus_write_tag_to_point(const neu_plugin_tag_value_t *tag, - modbus_point_write_t * point) -{ - int ret = NEU_ERR_SUCCESS; - ret = modbus_tag_to_point(tag->tag, &point->point); - point->value = tag->value; - return ret; -} + /** + * 对 Modbus 点进行排序并分组,生成读命令列表 + * + * @param tags 标签数组 + * @param max_byte 单次读取的最大字节数 + * @return 排序后的读命令列表 + */ + modbus_read_cmd_sort_t *modbus_tag_sort(UT_array * tags, + uint16_t max_byte) + { + modbus_read_max_byte = max_byte; + neu_tag_sort_result_t *result = + neu_tag_sort(tags, tag_sort, tag_cmp); + + modbus_read_cmd_sort_t *sort_result = + calloc(1, sizeof(modbus_read_cmd_sort_t)); + sort_result->n_cmd = result->n_sort; + sort_result->cmd = + calloc(result->n_sort, sizeof(modbus_read_cmd_t)); + + for (uint16_t i = 0; i < result->n_sort; i++) { + modbus_point_t *tag = + *(modbus_point_t **) utarray_front(result->sorts[i].tags); + struct modbus_sort_ctx *ctx = result->sorts[i].info.context; + + sort_result->cmd[i].tags = utarray_clone(result->sorts[i].tags); + sort_result->cmd[i].slave_id = tag->slave_id; + sort_result->cmd[i].area = tag->area; + sort_result->cmd[i].start_address = tag->start_address; + sort_result->cmd[i].n_register = ctx->end - ctx->start; + + free(result->sorts[i].info.context); + } -modbus_read_cmd_sort_t *modbus_tag_sort(UT_array *tags, uint16_t max_byte) -{ - modbus_read_max_byte = max_byte; - neu_tag_sort_result_t *result = neu_tag_sort(tags, tag_sort, tag_cmp); - - modbus_read_cmd_sort_t *sort_result = - calloc(1, sizeof(modbus_read_cmd_sort_t)); - sort_result->n_cmd = result->n_sort; - sort_result->cmd = calloc(result->n_sort, sizeof(modbus_read_cmd_t)); - - for (uint16_t i = 0; i < result->n_sort; i++) { - modbus_point_t *tag = - *(modbus_point_t **) utarray_front(result->sorts[i].tags); - struct modbus_sort_ctx *ctx = result->sorts[i].info.context; - - sort_result->cmd[i].tags = utarray_clone(result->sorts[i].tags); - sort_result->cmd[i].slave_id = tag->slave_id; - sort_result->cmd[i].area = tag->area; - sort_result->cmd[i].start_address = tag->start_address; - sort_result->cmd[i].n_register = ctx->end - ctx->start; - - free(result->sorts[i].info.context); - } - - neu_tag_sort_free(result); - return sort_result; -} - -int cal_n_byte(int type, neu_value_u *value, neu_datatag_addr_option_u option, - modbus_endianess_64 endianess_64, bool default_tag_endian_64) -{ - int n = 0; - switch (type) { - case NEU_TYPE_UINT16: - case NEU_TYPE_INT16: - n = sizeof(uint16_t); - value->u16 = htons(value->u16); - break; - case NEU_TYPE_FLOAT: - case NEU_TYPE_UINT32: - case NEU_TYPE_INT32: - n = sizeof(uint32_t); - value->u32 = htonl(value->u32); - break; - - case NEU_TYPE_DOUBLE: - case NEU_TYPE_INT64: - case NEU_TYPE_UINT64: - if (default_tag_endian_64) { - modbus_convert_endianess_64(value, endianess_64); + neu_tag_sort_free(result); + return sort_result; } - n = sizeof(uint64_t); - value->u64 = neu_htonll(value->u64); - break; - case NEU_TYPE_BIT: { - n = sizeof(uint8_t); - break; - } - case NEU_TYPE_STRING: { - n = option.string.length; - switch (option.string.type) { - case NEU_DATATAG_STRING_TYPE_H: - break; - case NEU_DATATAG_STRING_TYPE_L: - neu_datatag_string_ltoh(value->str, option.string.length); - break; - case NEU_DATATAG_STRING_TYPE_D: - break; - case NEU_DATATAG_STRING_TYPE_E: - break; + + /** + * 计算给定类型数据所需的字节数并进行字节序转换 + * + * @param type 数据类型 + * @param value 数据值,会被修改为网络字节序 + * @param option 地址选项 + * @param endianess_64 64位数据字节序 + * @param default_tag_endian_64 是否使用默认64位字节序 + * @return 字节数 + */ + int cal_n_byte( + int type, neu_value_u *value, neu_datatag_addr_option_u option, + modbus_endianess_64 endianess_64, bool default_tag_endian_64) + { + int n = 0; + switch (type) { + case NEU_TYPE_UINT16: + case NEU_TYPE_INT16: + n = sizeof(uint16_t); + value->u16 = htons(value->u16); + break; + case NEU_TYPE_FLOAT: + case NEU_TYPE_UINT32: + case NEU_TYPE_INT32: + n = sizeof(uint32_t); + value->u32 = htonl(value->u32); + break; + + case NEU_TYPE_DOUBLE: + case NEU_TYPE_INT64: + case NEU_TYPE_UINT64: + if (default_tag_endian_64) { + modbus_convert_endianess_64(value, endianess_64); + } + n = sizeof(uint64_t); + value->u64 = neu_htonll(value->u64); + break; + case NEU_TYPE_BIT: { + n = sizeof(uint8_t); + break; + } + case NEU_TYPE_STRING: { + n = option.string.length; + switch (option.string.type) { + case NEU_DATATAG_STRING_TYPE_H: + break; + case NEU_DATATAG_STRING_TYPE_L: + neu_datatag_string_ltoh(value->str, option.string.length); + break; + case NEU_DATATAG_STRING_TYPE_D: + break; + case NEU_DATATAG_STRING_TYPE_E: + break; + } + break; + } + case NEU_TYPE_BYTES: { + n = option.bytes.length; + break; + } + default: + assert(1 == 0); + break; + } + return n; } - break; - } - case NEU_TYPE_BYTES: { - n = option.bytes.length; - break; - } - default: - assert(1 == 0); - break; - } - return n; -} - -modbus_write_cmd_sort_t * -modbus_write_tags_sort(UT_array *tags, modbus_endianess_64 endianess_64) -{ - neu_tag_sort_result_t *result = - neu_tag_sort(tags, tag_sort_write, tag_cmp_write); - - modbus_write_cmd_sort_t *sort_result = - calloc(1, sizeof(modbus_write_cmd_sort_t)); - sort_result->n_cmd = result->n_sort; - sort_result->cmd = calloc(result->n_sort, sizeof(modbus_write_cmd_t)); - for (uint16_t i = 0; i < result->n_sort; i++) { - modbus_point_write_t *tag = - *(modbus_point_write_t **) utarray_front(result->sorts[i].tags); - struct modbus_sort_ctx *ctx = result->sorts[i].info.context; - - int num_tags = utarray_len(result->sorts[i].tags); - sort_result->cmd[i].bytes = calloc(num_tags, sizeof(neu_value_u)); - int n_byte = 0, n_byte_tag = 0; - uint8_t *data_bit = calloc((num_tags + 7) / 8, sizeof(uint8_t)); - int k = 0; - utarray_foreach(result->sorts[i].tags, modbus_point_write_t **, tag_s) + + /** + * 对写入点进行排序并分组,生成写命令列表 + * + * @param tags 标签数组 + * @param endianess_64 64位数据字节序 + * @return 排序后的写命令列表 + */ + modbus_write_cmd_sort_t *modbus_write_tags_sort( + UT_array * tags, modbus_endianess_64 endianess_64) { - if ((*tag_s)->point.area == MODBUS_AREA_COIL) { - n_byte_tag = - cal_n_byte((*tag_s)->point.type, &(*tag_s)->value, - (*tag_s)->point.option, endianess_64, true); - data_bit[k / 8] += ((*tag_s)->value.i8) << k % 8; - n_byte += n_byte_tag; - k++; - } else { - n_byte_tag = - cal_n_byte((*tag_s)->point.type, &(*tag_s)->value, - (*tag_s)->point.option, endianess_64, - (*tag_s)->point.option.value64.is_default); - memcpy(sort_result->cmd[i].bytes + - 2 * - ((*tag_s)->point.start_address - - tag->point.start_address), - &((*tag_s)->value), n_byte_tag); - n_byte += n_byte_tag; + neu_tag_sort_result_t *result = + neu_tag_sort(tags, tag_sort_write, tag_cmp_write); + + modbus_write_cmd_sort_t *sort_result = + calloc(1, sizeof(modbus_write_cmd_sort_t)); + sort_result->n_cmd = result->n_sort; + sort_result->cmd = + calloc(result->n_sort, sizeof(modbus_write_cmd_t)); + for (uint16_t i = 0; i < result->n_sort; i++) { + modbus_point_write_t *tag = + *(modbus_point_write_t **) utarray_front( + result->sorts[i].tags); + struct modbus_sort_ctx *ctx = result->sorts[i].info.context; + + int num_tags = utarray_len(result->sorts[i].tags); + sort_result->cmd[i].bytes = + calloc(num_tags, sizeof(neu_value_u)); + int n_byte = 0, n_byte_tag = 0; + uint8_t *data_bit = calloc((num_tags + 7) / 8, sizeof(uint8_t)); + int k = 0; + utarray_foreach(result->sorts[i].tags, modbus_point_write_t **, + tag_s) + { + if ((*tag_s)->point.area == MODBUS_AREA_COIL) { + n_byte_tag = cal_n_byte( + (*tag_s)->point.type, &(*tag_s)->value, + (*tag_s)->point.option, endianess_64, true); + data_bit[k / 8] += ((*tag_s)->value.i8) << k % 8; + n_byte += n_byte_tag; + k++; + } else { + n_byte_tag = cal_n_byte( + (*tag_s)->point.type, &(*tag_s)->value, + (*tag_s)->point.option, endianess_64, + (*tag_s)->point.option.value64.is_default); + memcpy(sort_result->cmd[i].bytes + + 2 * + ((*tag_s)->point.start_address - + tag->point.start_address), + &((*tag_s)->value), n_byte_tag); + n_byte += n_byte_tag; + } + } + if ((*(modbus_point_write_t **) utarray_front( + result->sorts[i].tags)) + ->point.area == MODBUS_AREA_COIL) { + memcpy(sort_result->cmd[i].bytes, data_bit, (k + 7) / 8); + } + + sort_result->cmd[i].tags = utarray_clone(result->sorts[i].tags); + sort_result->cmd[i].slave_id = tag->point.slave_id; + sort_result->cmd[i].area = tag->point.area; + sort_result->cmd[i].start_address = tag->point.start_address; + sort_result->cmd[i].n_register = ctx->end - ctx->start; + sort_result->cmd[i].n_byte = n_byte; + + free(data_bit); + free(result->sorts[i].info.context); } + + neu_tag_sort_free(result); + return sort_result; } - if ((*(modbus_point_write_t **) utarray_front(result->sorts[i].tags)) - ->point.area == MODBUS_AREA_COIL) { - memcpy(sort_result->cmd[i].bytes, data_bit, (k + 7) / 8); + + /** + * 释放 modbus_tag_sort 生成的资源 + * + * @param cs 读命令排序结构 + */ + void modbus_tag_sort_free(modbus_read_cmd_sort_t * cs) + { + for (uint16_t i = 0; i < cs->n_cmd; i++) { + utarray_free(cs->cmd[i].tags); + } + + free(cs->cmd); + free(cs); } - sort_result->cmd[i].tags = utarray_clone(result->sorts[i].tags); - sort_result->cmd[i].slave_id = tag->point.slave_id; - sort_result->cmd[i].area = tag->point.area; - sort_result->cmd[i].start_address = tag->point.start_address; - sort_result->cmd[i].n_register = ctx->end - ctx->start; - sort_result->cmd[i].n_byte = n_byte; + static int tag_cmp(neu_tag_sort_elem_t * tag1, + neu_tag_sort_elem_t * tag2) + { + modbus_point_t *p_t1 = (modbus_point_t *) tag1->tag; + modbus_point_t *p_t2 = (modbus_point_t *) tag2->tag; - free(data_bit); - free(result->sorts[i].info.context); - } + if (p_t1->slave_id > p_t2->slave_id) { + return 1; + } else if (p_t1->slave_id < p_t2->slave_id) { + return -1; + } - neu_tag_sort_free(result); - return sort_result; -} + if (p_t1->area > p_t2->area) { + return 1; + } else if (p_t1->area < p_t2->area) { + return -1; + } -void modbus_tag_sort_free(modbus_read_cmd_sort_t *cs) -{ - for (uint16_t i = 0; i < cs->n_cmd; i++) { - utarray_free(cs->cmd[i].tags); - } + if (p_t1->start_address > p_t2->start_address) { + return 1; + } else if (p_t1->start_address < p_t2->start_address) { + return -1; + } - free(cs->cmd); - free(cs); -} + if (p_t1->n_register > p_t2->n_register) { + return 1; + } else if (p_t1->n_register < p_t2->n_register) { + return -1; + } -static int tag_cmp(neu_tag_sort_elem_t *tag1, neu_tag_sort_elem_t *tag2) -{ - modbus_point_t *p_t1 = (modbus_point_t *) tag1->tag; - modbus_point_t *p_t2 = (modbus_point_t *) tag2->tag; - - if (p_t1->slave_id > p_t2->slave_id) { - return 1; - } else if (p_t1->slave_id < p_t2->slave_id) { - return -1; - } - - if (p_t1->area > p_t2->area) { - return 1; - } else if (p_t1->area < p_t2->area) { - return -1; - } - - if (p_t1->start_address > p_t2->start_address) { - return 1; - } else if (p_t1->start_address < p_t2->start_address) { - return -1; - } - - if (p_t1->n_register > p_t2->n_register) { - return 1; - } else if (p_t1->n_register < p_t2->n_register) { - return -1; - } - - return 0; -} - -static bool tag_sort(neu_tag_sort_t *sort, void *tag, void *tag_to_be_sorted) -{ - modbus_point_t * t1 = (modbus_point_t *) tag; - modbus_point_t * t2 = (modbus_point_t *) tag_to_be_sorted; - struct modbus_sort_ctx *ctx = NULL; - - if (sort->info.context == NULL) { - sort->info.context = calloc(1, sizeof(struct modbus_sort_ctx)); - ctx = (struct modbus_sort_ctx *) sort->info.context; - ctx->start = t1->start_address; - ctx->end = t1->start_address + t1->n_register; - return true; - } - - ctx = (struct modbus_sort_ctx *) sort->info.context; - - if (t1->slave_id != t2->slave_id) { - return false; - } - - if (t1->area != t2->area) { - return false; - } - - if (t2->start_address > ctx->end) { - return false; - } - - switch (t1->area) { - case MODBUS_AREA_COIL: - case MODBUS_AREA_INPUT: - if ((ctx->end - ctx->start + 7) / 8 >= modbus_read_max_byte) { - return false; - } - break; - case MODBUS_AREA_INPUT_REGISTER: - case MODBUS_AREA_HOLD_REGISTER: { - uint16_t now_bytes = (ctx->end - ctx->start) * 2; - uint16_t add_now = now_bytes + t2->n_register * 2; - if (add_now >= modbus_read_max_byte) { - return false; + return 0; } - break; - } - } + static bool tag_sort(neu_tag_sort_t * sort, void *tag, + void *tag_to_be_sorted) + { + modbus_point_t *t1 = (modbus_point_t *) tag; + modbus_point_t *t2 = (modbus_point_t *) tag_to_be_sorted; + struct modbus_sort_ctx *ctx = NULL; + + if (sort->info.context == NULL) { + sort->info.context = calloc(1, sizeof(struct modbus_sort_ctx)); + ctx = (struct modbus_sort_ctx *) sort->info.context; + ctx->start = t1->start_address; + ctx->end = t1->start_address + t1->n_register; + return true; + } + + ctx = (struct modbus_sort_ctx *) sort->info.context; - if (t2->start_address + t2->n_register > ctx->end) { - ctx->end = t2->start_address + t2->n_register; - } + if (t1->slave_id != t2->slave_id) { + return false; + } - return true; -} + if (t1->area != t2->area) { + return false; + } -static int tag_cmp_write(neu_tag_sort_elem_t *tag1, neu_tag_sort_elem_t *tag2) -{ - modbus_point_write_t *p_t1 = (modbus_point_write_t *) tag1->tag; - modbus_point_write_t *p_t2 = (modbus_point_write_t *) tag2->tag; - - if (p_t1->point.slave_id > p_t2->point.slave_id) { - return 1; - } else if (p_t1->point.slave_id < p_t2->point.slave_id) { - return -1; - } - - if (p_t1->point.area > p_t2->point.area) { - return 1; - } else if (p_t1->point.area < p_t2->point.area) { - return -1; - } - - if (p_t1->point.start_address > p_t2->point.start_address) { - return 1; - } else if (p_t1->point.start_address < p_t2->point.start_address) { - return -1; - } - - if (p_t1->point.n_register > p_t2->point.n_register) { - return 1; - } else if (p_t1->point.n_register < p_t2->point.n_register) { - return -1; - } - - return 0; -} - -static bool tag_sort_write(neu_tag_sort_t *sort, void *tag, - void *tag_to_be_sorted) -{ - modbus_point_write_t * t1 = (modbus_point_write_t *) tag; - modbus_point_write_t * t2 = (modbus_point_write_t *) tag_to_be_sorted; - struct modbus_sort_ctx *ctx = NULL; - - if (sort->info.context == NULL) { - sort->info.context = calloc(1, sizeof(struct modbus_sort_ctx)); - ctx = (struct modbus_sort_ctx *) sort->info.context; - ctx->start = t1->point.start_address; - ctx->end = t1->point.start_address + t1->point.n_register; - return true; - } - - ctx = (struct modbus_sort_ctx *) sort->info.context; - - if (t1->point.slave_id != t2->point.slave_id) { - return false; - } - - if (t1->point.area != t2->point.area) { - return false; - } - - if (t2->point.start_address > ctx->end) { - return false; - } - - switch (t1->point.area) { - case MODBUS_AREA_COIL: - case MODBUS_AREA_INPUT: - if ((ctx->end - ctx->start) / 8 >= modbus_read_max_byte - 1) { - return false; + if (t2->start_address > ctx->end) { + return false; + } + + switch (t1->area) { + case MODBUS_AREA_COIL: + case MODBUS_AREA_INPUT: + if ((ctx->end - ctx->start + 7) / 8 >= modbus_read_max_byte) { + return false; + } + break; + case MODBUS_AREA_INPUT_REGISTER: + case MODBUS_AREA_HOLD_REGISTER: { + uint16_t now_bytes = (ctx->end - ctx->start) * 2; + uint16_t add_now = now_bytes + t2->n_register * 2; + if (add_now >= modbus_read_max_byte) { + return false; + } + + break; + } + } + + if (t2->start_address + t2->n_register > ctx->end) { + ctx->end = t2->start_address + t2->n_register; + } + + return true; } - break; - case MODBUS_AREA_INPUT_REGISTER: - case MODBUS_AREA_HOLD_REGISTER: { - uint16_t now_bytes = (ctx->end - ctx->start) * 2; - uint16_t add_now = now_bytes + t2->point.n_register * 2; - if (add_now >= modbus_read_max_byte) { - return false; + + static int tag_cmp_write(neu_tag_sort_elem_t * tag1, + neu_tag_sort_elem_t * tag2) + { + modbus_point_write_t *p_t1 = (modbus_point_write_t *) tag1->tag; + modbus_point_write_t *p_t2 = (modbus_point_write_t *) tag2->tag; + + if (p_t1->point.slave_id > p_t2->point.slave_id) { + return 1; + } else if (p_t1->point.slave_id < p_t2->point.slave_id) { + return -1; + } + + if (p_t1->point.area > p_t2->point.area) { + return 1; + } else if (p_t1->point.area < p_t2->point.area) { + return -1; + } + + if (p_t1->point.start_address > p_t2->point.start_address) { + return 1; + } else if (p_t1->point.start_address < p_t2->point.start_address) { + return -1; + } + + if (p_t1->point.n_register > p_t2->point.n_register) { + return 1; + } else if (p_t1->point.n_register < p_t2->point.n_register) { + return -1; + } + + return 0; } - break; - } - } + static bool tag_sort_write(neu_tag_sort_t * sort, void *tag, + void *tag_to_be_sorted) + { + modbus_point_write_t *t1 = (modbus_point_write_t *) tag; + modbus_point_write_t *t2 = + (modbus_point_write_t *) tag_to_be_sorted; + struct modbus_sort_ctx *ctx = NULL; + + if (sort->info.context == NULL) { + sort->info.context = calloc(1, sizeof(struct modbus_sort_ctx)); + ctx = (struct modbus_sort_ctx *) sort->info.context; + ctx->start = t1->point.start_address; + ctx->end = t1->point.start_address + t1->point.n_register; + return true; + } + + ctx = (struct modbus_sort_ctx *) sort->info.context; - if (t2->point.start_address + t2->point.n_register > ctx->end) { - ctx->end = t2->point.start_address + t2->point.n_register; - } + if (t1->point.slave_id != t2->point.slave_id) { + return false; + } - return true; -} + if (t1->point.area != t2->point.area) { + return false; + } -void modbus_convert_endianess_64(neu_value_u * value, - modbus_endianess_64 endianess_64) -{ - switch (endianess_64) { - case MODBUS_LL: - break; - case MODBUS_BB: - value->u64 = neu_htonll(value->u64); - break; - case MODBUS_LB: - value->u64 = neu_htonlb(value->u64); - break; - case MODBUS_BL: - value->u64 = neu_htonbl(value->u64); - break; - default: - break; - } -} \ No newline at end of file + if (t2->point.start_address > ctx->end) { + return false; + } + + switch (t1->point.area) { + case MODBUS_AREA_COIL: + case MODBUS_AREA_INPUT: + if ((ctx->end - ctx->start) / 8 >= modbus_read_max_byte - 1) { + return false; + } + break; + case MODBUS_AREA_INPUT_REGISTER: + case MODBUS_AREA_HOLD_REGISTER: { + uint16_t now_bytes = (ctx->end - ctx->start) * 2; + uint16_t add_now = now_bytes + t2->point.n_register * 2; + if (add_now >= modbus_read_max_byte) { + return false; + } + + break; + } + } + + if (t2->point.start_address + t2->point.n_register > ctx->end) { + ctx->end = t2->point.start_address + t2->point.n_register; + } + + return true; + } + + void modbus_convert_endianess_64(neu_value_u * value, + modbus_endianess_64 endianess_64) + { + switch (endianess_64) { + case MODBUS_LL: + break; + case MODBUS_BB: + value->u64 = neu_htonll(value->u64); + break; + case MODBUS_LB: + value->u64 = neu_htonlb(value->u64); + break; + case MODBUS_BL: + value->u64 = neu_htonbl(value->u64); + break; + default: + break; + } + } \ No newline at end of file diff --git a/plugins/modbus/modbus_point.h b/plugins/modbus/modbus_point.h index 5f0497b4e..ebaea18b2 100644 --- a/plugins/modbus/modbus_point.h +++ b/plugins/modbus/modbus_point.h @@ -19,6 +19,12 @@ #ifndef _NEU_PLUGIN_MODBUS_POINT_H_ #define _NEU_PLUGIN_MODBUS_POINT_H_ +/** + * modbus_point.h + * 本文件定义了 Modbus 点(数据点)结构体及相关函数 + * 用于描述和操作 Modbus 协议的数据点,支持读写和批量操作 + */ + #ifdef __cplusplus extern "C" { #endif @@ -29,62 +35,122 @@ extern "C" { #include "modbus.h" +/** + * Modbus 点结构体,描述单个 Modbus 数据点 + */ typedef struct modbus_point { - uint8_t slave_id; - modbus_area_e area; - uint16_t start_address; - uint16_t n_register; - - neu_type_e type; - neu_datatag_addr_option_u option; - char name[NEU_TAG_NAME_LEN]; + uint8_t slave_id; // 从站 ID + modbus_area_e area; // Modbus 区域类型(线圈、输入、保持寄存器等) + uint16_t start_address; // 起始地址 + uint16_t n_register; // 寄存器数量 + + neu_type_e type; // 数据类型 + neu_datatag_addr_option_u option; // 地址选项 + char name[NEU_TAG_NAME_LEN]; // 点名称 } modbus_point_t; +/** + * Modbus 写入点结构体,包含点信息和写入值 + */ typedef struct modbus_point_write { - modbus_point_t point; - neu_value_u value; + modbus_point_t point; // Modbus 点信息 + neu_value_u value; // 写入的值 } modbus_point_write_t; +/** + * 将通用数据标签转换为 Modbus 点 + * + * @param tag 数据标签 + * @param point 输出 Modbus 点 + * @return 成功返回 NEU_ERR_SUCCESS,失败返回错误码 + */ int modbus_tag_to_point(const neu_datatag_t *tag, modbus_point_t *point); + +/** + * 将包含值的数据标签转换为写入点 + * + * @param tag 包含值的数据标签 + * @param point 输出写入点 + * @return 成功返回 NEU_ERR_SUCCESS,失败返回错误码 + */ int modbus_write_tag_to_point(const neu_plugin_tag_value_t *tag, - modbus_point_write_t * point); + modbus_point_write_t *point); +/** + * Modbus 读命令结构体,表示单个批量读请求 + */ typedef struct modbus_read_cmd { - uint8_t slave_id; - modbus_area_e area; - uint16_t start_address; - uint16_t n_register; + uint8_t slave_id; // 从站 ID + modbus_area_e area; // Modbus 区域类型 + uint16_t start_address; // 起始地址 + uint16_t n_register; // 寄存器数量 - UT_array *tags; // modbus_point_t ptr; + UT_array *tags; // modbus_point_t 指针数组 } modbus_read_cmd_t; +/** + * Modbus 读命令排序结构体,包含多个读命令 + */ typedef struct modbus_read_cmd_sort { - uint16_t n_cmd; - modbus_read_cmd_t *cmd; + uint16_t n_cmd; // 命令数量 + modbus_read_cmd_t *cmd; // 命令数组 } modbus_read_cmd_sort_t; +/** + * Modbus 写命令结构体,表示单个批量写请求 + */ typedef struct modbus_write_cmd { - uint8_t slave_id; - modbus_area_e area; - uint16_t start_address; - uint16_t n_register; - uint8_t n_byte; - uint8_t * bytes; - - UT_array *tags; + uint8_t slave_id; // 从站 ID + modbus_area_e area; // Modbus 区域类型 + uint16_t start_address; // 起始地址 + uint16_t n_register; // 寄存器数量 + uint8_t n_byte; // 字节数 + uint8_t *bytes; // 数据字节数组 + + UT_array *tags; // modbus_point_write_t 指针数组 } modbus_write_cmd_t; +/** + * Modbus 写命令排序结构体,包含多个写命令 + */ typedef struct modbus_write_cmd_sort { - uint16_t n_cmd; - modbus_write_cmd_t *cmd; + uint16_t n_cmd; // 命令数量 + modbus_write_cmd_t *cmd; // 命令数组 } modbus_write_cmd_sort_t; +/** + * 对 Modbus 点进行排序并分组,生成读命令列表 + * + * @param tags 标签数组 + * @param max_byte 单次读取的最大字节数 + * @return 排序后的读命令列表 + */ modbus_read_cmd_sort_t *modbus_tag_sort(UT_array *tags, uint16_t max_byte); + +/** + * 对写入点进行排序并分组,生成写命令列表 + * + * @param tags 标签数组 + * @param endianess_64 64位数据字节序 + * @return 排序后的写命令列表 + */ modbus_write_cmd_sort_t * - modbus_write_tags_sort(UT_array *tags, modbus_endianess_64 endianess_64); +modbus_write_tags_sort(UT_array *tags, modbus_endianess_64 endianess_64); + +/** + * 释放 modbus_tag_sort 生成的资源 + * + * @param cs 读命令排序结构 + */ void modbus_tag_sort_free(modbus_read_cmd_sort_t *cs); -void modbus_convert_endianess_64(neu_value_u * value, +/** + * 转换64位数据的字节序 + * + * @param value 数据值,会被修改 + * @param endianess_64 目标字节序 + */ +void modbus_convert_endianess_64(neu_value_u *value, modbus_endianess_64 endianess_64); #ifdef __cplusplus diff --git a/plugins/modbus/modbus_req.c b/plugins/modbus/modbus_req.c index 9a82267ce..0e477432b 100644 --- a/plugins/modbus/modbus_req.c +++ b/plugins/modbus/modbus_req.c @@ -23,15 +23,27 @@ #include "modbus_req.h" +/** + * modbus_req.c + * 本文件实现了 Modbus 协议插件的请求处理、连接管理、分组定时器等功能, + * 是插件与 Modbus 协议栈交互的核心部分。 + */ + +/** + * Modbus 分组数据结构,用于存储分组的标签和命令 + */ struct modbus_group_data { - UT_array * tags; - char * group; - modbus_read_cmd_sort_t *cmd_sort; + UT_array *tags; // 标签数组 + char *group; // 分组名称 + modbus_read_cmd_sort_t *cmd_sort; // 排序后的命令 }; +/** + * Modbus 写入标签数据结构,用于存储批量写入的标签和命令 + */ struct modbus_write_tags_data { - UT_array * tags; - modbus_write_cmd_sort_t *cmd_sort; + UT_array *tags; // 标签数组 + modbus_write_cmd_sort_t *cmd_sort; // 排序后的写命令 }; static void plugin_group_free(neu_plugin_group_t *pgp); @@ -56,11 +68,11 @@ void modbus_conn_disconnected(void *data, int fd) void modbus_tcp_server_listen(void *data, int fd) { - struct neu_plugin * plugin = (struct neu_plugin *) data; + struct neu_plugin *plugin = (struct neu_plugin *) data; neu_event_io_param_t param = { - .cb = modbus_tcp_server_io_callback, - .fd = fd, - .usr_data = (void *) plugin, + .cb = modbus_tcp_server_io_callback, + .fd = fd, + .usr_data = (void *) plugin, }; plugin->tcp_server_io = neu_event_add_io(plugin->events, param); @@ -326,7 +338,7 @@ int modbus_group_timer(neu_plugin_t *plugin, neu_plugin_group_t *group, int modbus_value_handle(void *ctx, uint8_t slave_id, uint16_t n_byte, uint8_t *bytes, int error) { - neu_plugin_t * plugin = (neu_plugin_t *) ctx; + neu_plugin_t *plugin = (neu_plugin_t *) ctx; struct modbus_group_data *gd = (struct modbus_group_data *) plugin->plugin_group_data; uint16_t start_address = gd->cmd_sort->cmd[plugin->cmd_idx].start_address; @@ -590,7 +602,10 @@ int modbus_write_tags(neu_plugin_t *plugin, void *req, UT_array *tags) } free(gtags->cmd_sort->cmd); free(gtags->cmd_sort); - utarray_foreach(gtags->tags, modbus_point_write_t **, tag) { free(*tag); } + utarray_foreach(gtags->tags, modbus_point_write_t **, tag) + { + free(*tag); + } utarray_free(gtags->tags); free(gtags); return ret; @@ -611,7 +626,10 @@ static void plugin_group_free(neu_plugin_group_t *pgp) modbus_tag_sort_free(gd->cmd_sort); - utarray_foreach(gd->tags, modbus_point_t **, tag) { free(*tag); } + utarray_foreach(gd->tags, modbus_point_t **, tag) + { + free(*tag); + } utarray_free(gd->tags); free(gd->group); @@ -622,7 +640,7 @@ static void plugin_group_free(neu_plugin_group_t *pgp) static int process_protocol_buf(neu_plugin_t *plugin, uint8_t slave_id, uint16_t response_size) { - uint8_t * recv_buf = calloc(response_size, 1); + uint8_t *recv_buf = calloc(response_size, 1); neu_protocol_unpack_buf_t pbuf = { 0 }; ssize_t ret = 0; if (plugin->protocol == MODBUS_PROTOCOL_TCP) { diff --git a/plugins/modbus/modbus_req.h b/plugins/modbus/modbus_req.h index ed6ee3e96..a5fd6742a 100644 --- a/plugins/modbus/modbus_req.h +++ b/plugins/modbus/modbus_req.h @@ -19,51 +19,148 @@ #ifndef _NEU_M_PLUGIN_MODBUS_REQ_H_ #define _NEU_M_PLUGIN_MODBUS_REQ_H_ +/** + * modbus_req.h + * 本文件定义了 Modbus 协议插件的请求处理、连接管理、定时器等接口, + * 负责处理 Modbus 协议的各种请求和响应,是插件与协议栈交互的核心。 + */ + #include #include "modbus_stack.h" +/** + * Modbus 插件结构体,包含插件的所有状态和配置 + */ struct neu_plugin { - neu_plugin_common_t common; + neu_plugin_common_t common; // 通用插件部分 - neu_conn_t * conn; - modbus_stack_t *stack; + neu_conn_t * conn; // 连接对象 + modbus_stack_t *stack; // Modbus 协议栈 - void * plugin_group_data; - uint16_t cmd_idx; + void * plugin_group_data; // 插件组数据 + uint16_t cmd_idx; // 当前命令索引 - neu_event_io_t *tcp_server_io; - bool is_server; - bool is_serial; - int client_fd; - neu_events_t * events; + neu_event_io_t *tcp_server_io; // TCP 服务器 IO 事件 + bool is_server; // 是否为服务器模式 + bool is_serial; // 是否为串口模式 + int client_fd; // 客户端文件描述符 + neu_events_t * events; // 事件系统 - modbus_protocol_e protocol; + modbus_protocol_e protocol; // 协议类型 - uint16_t interval; - uint16_t retry_interval; - uint16_t max_retries; + uint16_t interval; // 轮询间隔 + uint16_t retry_interval; // 重试间隔 + uint16_t max_retries; // 最大重试次数 - modbus_endianess_64 endianess_64; + modbus_endianess_64 endianess_64; // 64位数据字节序 }; +/** + * 连接成功的回调函数 + * @param data 插件指针 + * @param fd 连接文件描述符 + */ void modbus_conn_connected(void *data, int fd); + +/** + * 连接断开的回调函数 + * @param data 插件指针 + * @param fd 连接文件描述符 + */ void modbus_conn_disconnected(void *data, int fd); + +/** + * TCP服务器监听回调函数 + * @param data 插件指针 + * @param fd 监听文件描述符 + */ void modbus_tcp_server_listen(void *data, int fd); + +/** + * TCP服务器停止监听回调函数 + * @param data 插件指针 + * @param fd 监听文件描述符 + */ void modbus_tcp_server_stop(void *data, int fd); + +/** + * TCP服务器IO事件回调函数 + * @param type IO事件类型 + * @param fd 文件描述符 + * @param usr_data 用户数据 + * @return 处理结果 + */ int modbus_tcp_server_io_callback(enum neu_event_io_type type, int fd, void *usr_data); +/** + * 处理分组定时器,发送读请求并处理响应 + * @param plugin 插件指针 + * @param group 分组 + * @param max_byte 最大字节数 + * @return 处理结果 + */ int modbus_group_timer(neu_plugin_t *plugin, neu_plugin_group_t *group, uint16_t max_byte); + +/** + * 发送消息回调函数 + * @param ctx 上下文指针 + * @param n_byte 发送的字节数 + * @param bytes 发送的数据 + * @return 成功返回发送的字节数,失败返回负数 + */ int modbus_send_msg(void *ctx, uint16_t n_byte, uint8_t *bytes); + +/** + * 处理接收到的数据值 + * @param ctx 上下文指针 + * @param slave_id 从站 ID + * @param n_byte 接收的字节数 + */ int modbus_value_handle(void *ctx, uint8_t slave_id, uint16_t n_byte, uint8_t *bytes, int error); + +/** + * 执行写操作 + * @param plugin 插件指针 + * @param req 请求上下文 + * @param tag 标签 + * @param value 写入值 + * @param response 是否需要响应 + * @return 处理结果 + */ int modbus_write(neu_plugin_t *plugin, void *req, neu_datatag_t *tag, neu_value_u value, bool response); + +/** + * 写入单个标签 + * @param plugin 插件指针 + * @param req 请求上下文 + * @param tag 标签 + * @param value 写入值 + * @return 处理结果 + */ int modbus_write_tag(neu_plugin_t *plugin, void *req, neu_datatag_t *tag, neu_value_u value); + +/** + * 批量写入标签 + * @param plugin 插件指针 + * @param req 请求上下文 + * @param tags 标签数组 + * @return 处理结果 + */ int modbus_write_tags(neu_plugin_t *plugin, void *req, UT_array *tags); + +/** + * 处理写响应的回调函数 + * @param ctx 上下文指针 + * @param req 请求上下文 + * @param error 错误码 + * @return 处理结果 + */ int modbus_write_resp(void *ctx, void *req, int error); #endif diff --git a/plugins/modbus/modbus_rtu.c b/plugins/modbus/modbus_rtu.c index 184335db8..2c6c636a0 100644 --- a/plugins/modbus/modbus_rtu.c +++ b/plugins/modbus/modbus_rtu.c @@ -26,22 +26,118 @@ #include "modbus_req.h" #include "modbus_stack.h" +/** + * modbus_rtu.c + * 本文件实现了 Modbus RTU 协议插件的驱动接口,包括: + * - 初始化和配置 + * - 启动和停止 + * - 标签校验 + * - 分组定时器处理 + * - 写入操作 + * 支持串口和 TCP 两种通信方式,以 RTU 协议格式传输。 + */ + +/** + * 创建驱动插件 + * @return 插件对象指针 + */ static neu_plugin_t *driver_open(void); +/** + * 关闭驱动插件 + * @param plugin 插件对象 + * @return 处理结果 + */ static int driver_close(neu_plugin_t *plugin); + +/** + * 初始化驱动插件 + * @param plugin 插件对象 + * @param load 是否加载配置 + * @return 处理结果 + */ static int driver_init(neu_plugin_t *plugin, bool load); + +/** + * 反初始化驱动插件 + * @param plugin 插件对象 + * @return 处理结果 + */ static int driver_uninit(neu_plugin_t *plugin); + +/** + * 启动驱动插件 + * @param plugin 插件对象 + * @return 处理结果 + */ static int driver_start(neu_plugin_t *plugin); + +/** + * 停止驱动插件 + * @param plugin 插件对象 + * @return 处理结果 + */ static int driver_stop(neu_plugin_t *plugin); + +/** + * 配置驱动插件 + * @param plugin 插件对象 + * @param config 配置字符串 + * @return 处理结果 + */ static int driver_config(neu_plugin_t *plugin, const char *config); + +/** + * 处理请求 + * @param plugin 插件对象 + * @param head 请求头 + * @param data 请求数据 + * @return 处理结果 + */ static int driver_request(neu_plugin_t *plugin, neu_reqresp_head_t *head, void *data); +/** + * 验证标签格式 + * @param tag 标签 + * @return 验证结果 + */ static int driver_tag_validator(const neu_datatag_t *tag); + +/** + * 验证标签 + * @param plugin 插件对象 + * @param tag 标签 + * @return 验证结果 + */ static int driver_validate_tag(neu_plugin_t *plugin, neu_datatag_t *tag); + +/** + * 分组定时器处理 + * @param plugin 插件对象 + * @param group 分组 + * @return 处理结果 + */ static int driver_group_timer(neu_plugin_t *plugin, neu_plugin_group_t *group); + +/** + * 写入单个标签 + * @param plugin 插件对象 + * @param req 请求上下文 + * @param tag 标签 + * @param value 写入值 + * @return 处理结果 + */ static int driver_write(neu_plugin_t *plugin, void *req, neu_datatag_t *tag, neu_value_u value); + +/** + * 批量写入标签 + * @param plugin 插件对象 + * @param req 请求上下文 + * @param tags 标签数组 + * @return 处理结果 + */ static int driver_write_tags(neu_plugin_t *plugin, void *req, UT_array *tags); static const neu_plugin_intf_funs_t plugin_intf_funs = { @@ -104,8 +200,8 @@ static int driver_init(neu_plugin_t *plugin, bool load) plugin->protocol = MODBUS_PROTOCOL_RTU; plugin->events = neu_event_new(); plugin->stack = modbus_stack_create((void *) plugin, MODBUS_PROTOCOL_RTU, - modbus_send_msg, modbus_value_handle, - modbus_write_resp); + modbus_send_msg, modbus_value_handle, + modbus_write_resp); plog_notice(plugin, "%s init success", plugin->common.name); return 0; @@ -146,7 +242,7 @@ static int driver_stop(neu_plugin_t *plugin) static int driver_config(neu_plugin_t *plugin, const char *config) { int ret = 0; - char * err_param = NULL; + char *err_param = NULL; neu_conn_param_t param = { 0 }; neu_json_elem_t link = { .name = "link", .t = NEU_JSON_INT }; diff --git a/plugins/modbus/modbus_stack.c b/plugins/modbus/modbus_stack.c index cdb4249b7..4b00257e7 100644 --- a/plugins/modbus/modbus_stack.c +++ b/plugins/modbus/modbus_stack.c @@ -23,20 +23,42 @@ #include "modbus_req.h" #include "modbus_stack.h" +/** + * modbus_stack.c + * 本文件实现了 Modbus 协议栈的核心功能,包括: + * - 协议栈的创建和销毁 + * - 数据收发操作 + * - 读写命令的处理 + * - TCP/RTU 协议的支持 + */ + +/** + * Modbus 协议栈结构体 + */ struct modbus_stack { - void * ctx; - modbus_stack_send send_fn; - modbus_stack_value value_fn; - modbus_stack_write_resp write_resp; + void *ctx; // 上下文指针 + modbus_stack_send send_fn; // 发送数据的回调函数 + modbus_stack_value value_fn; // 处理值的回调函数 + modbus_stack_write_resp write_resp; // 处理写响应的回调函数 - modbus_protocol_e protocol; - uint16_t read_seq; - uint16_t write_seq; + modbus_protocol_e protocol; // 协议类型:TCP 或 RTU + uint16_t read_seq; // 读请求序列号 + uint16_t write_seq; // 写请求序列号 - uint8_t *buf; - uint16_t buf_size; + uint8_t *buf; // 数据缓冲区 + uint16_t buf_size; // 缓冲区大小 }; +/** + * 创建 Modbus 协议栈对象 + * + * @param ctx 上下文指针 + * @param protocol 协议类型:TCP 或 RTU + * @param send_fn 发送数据的回调函数 + * @param value_fn 处理值的回调函数 + * @param write_resp 处理写响应的回调函数 + * @return 协议栈对象指针 + */ modbus_stack_t *modbus_stack_create(void *ctx, modbus_protocol_e protocol, modbus_stack_send send_fn, modbus_stack_value value_fn, @@ -56,12 +78,25 @@ modbus_stack_t *modbus_stack_create(void *ctx, modbus_protocol_e protocol, return stack; } +/** + * 销毁 Modbus 协议栈对象并释放资源 + * + * @param stack 协议栈对象 + */ void modbus_stack_destroy(modbus_stack_t *stack) { free(stack->buf); free(stack); } +/** + * 处理接收到的 Modbus 数据包 + * + * @param stack 协议栈对象 + * @param slave_id 从站 ID + * @param buf 数据缓冲区 + * @return 成功返回数据包大小,失败返回负数 + */ int modbus_stack_recv(modbus_stack_t *stack, uint8_t slave_id, neu_protocol_unpack_buf_t *buf) { @@ -94,7 +129,7 @@ int modbus_stack_recv(modbus_stack_t *stack, uint8_t slave_id, case MODBUS_READ_HOLD_REG: case MODBUS_READ_INPUT_REG: { struct modbus_data data = { 0 }; - uint8_t * bytes = NULL; + uint8_t *bytes = NULL; ret = modbus_data_unwrap(buf, &data); if (ret <= 0) { return -1; @@ -177,6 +212,17 @@ int modbus_stack_recv(modbus_stack_t *stack, uint8_t slave_id, return neu_protocol_unpack_buf_used_size(buf); } +/** + * 发送 Modbus 读请求 + * + * @param stack 协议栈对象 + * @param slave_id 从站 ID + * @param area Modbus 区域类型 + * @param start_address 起始地址 + * @param n_reg 寄存器数量 + * @param response_size 返回预期响应大小 + * @return 成功返回发送的字节数,失败返回负数 + */ int modbus_stack_read(modbus_stack_t *stack, uint8_t slave_id, enum modbus_area area, uint16_t start_address, uint16_t n_reg, uint16_t *response_size) @@ -238,6 +284,21 @@ int modbus_stack_read(modbus_stack_t *stack, uint8_t slave_id, return ret; } +/** + * 发送 Modbus 写请求 + * + * @param stack 协议栈对象 + * @param req 请求上下文 + * @param slave_id 从站 ID + * @param area Modbus 区域类型 + * @param start_address 起始地址 + * @param n_reg 寄存器数量 + * @param bytes 写入数据 + * @param n_byte 数据字节数 + * @param response_size 返回预期响应大小 + * @param response 是否需要响应 + * @return 成功返回发送的字节数,失败返回负数 + */ int modbus_stack_write(modbus_stack_t *stack, void *req, uint8_t slave_id, enum modbus_area area, uint16_t start_address, uint16_t n_reg, uint8_t *bytes, uint8_t n_byte, @@ -314,6 +375,12 @@ int modbus_stack_write(modbus_stack_t *stack, void *req, uint8_t slave_id, return ret; } +/** + * 判断协议栈是否为 RTU 模式 + * + * @param stack 协议栈对象 + * @return 如果是 RTU 模式则返回 true,否则返回 false + */ bool modbus_stack_is_rtu(modbus_stack_t *stack) { return stack->protocol == MODBUS_PROTOCOL_RTU; diff --git a/plugins/modbus/modbus_stack.h b/plugins/modbus/modbus_stack.h index 8e1df0a74..5c6793425 100644 --- a/plugins/modbus/modbus_stack.h +++ b/plugins/modbus/modbus_stack.h @@ -19,6 +19,12 @@ #ifndef _NEU_M_PLUGIN_MODBUS_STACK_H_ #define _NEU_M_PLUGIN_MODBUS_STACK_H_ +/** + * modbus_stack.h + * 本文件定义了 Modbus 协议栈的接口和数据结构,用于封装 Modbus TCP/RTU 协议的通信功能。 + * 提供了协议栈的创建、销毁、发送和接收数据的函数。 + */ + #include #include @@ -27,32 +33,118 @@ typedef struct modbus_stack modbus_stack_t; +/** + * 发送数据的回调函数类型 + * @param ctx 上下文指针 + * @param n_byte 发送的字节数 + * @param bytes 发送的数据 + * @return 成功返回发送的字节数,失败返回负数 + */ typedef int (*modbus_stack_send)(void *ctx, uint16_t n_byte, uint8_t *bytes); + +/** + * 处理接收到的数据值的回调函数类型 + * @param ctx 上下文指针 + * @param slave_id 从站 ID + * @param n_byte 接收的字节数 + * @param bytes 接收的数据 + * @param error 错误码 + * @return 处理结果 + */ typedef int (*modbus_stack_value)(void *ctx, uint8_t slave_id, uint16_t n_byte, uint8_t *bytes, int error); + +/** + * 处理写响应的回调函数类型 + * @param ctx 上下文指针 + * @param req 请求上下文 + * @param error 错误码 + * @return 处理结果 + */ typedef int (*modbus_stack_write_resp)(void *ctx, void *req, int error); +/** + * Modbus 协议类型枚举 + */ typedef enum modbus_protocol { - MODBUS_PROTOCOL_TCP = 1, - MODBUS_PROTOCOL_RTU = 2, + MODBUS_PROTOCOL_TCP = 1, // Modbus TCP 协议 + MODBUS_PROTOCOL_RTU = 2, // Modbus RTU 协议 } modbus_protocol_e; +/** + * 创建 Modbus 协议栈对象 + * + * @param ctx 上下文指针 + * @param protocol 协议类型:TCP 或 RTU + * @param send_fn 发送数据的回调函数 + * @param value_fn 处理值的回调函数 + * @param write_resp 处理写响应的回调函数 + * @return 协议栈对象指针 + */ modbus_stack_t *modbus_stack_create(void *ctx, modbus_protocol_e protocol, modbus_stack_send send_fn, modbus_stack_value value_fn, modbus_stack_write_resp write_resp); + +/** + * 销毁 Modbus 协议栈对象并释放资源 + * + * @param stack 协议栈对象 + */ void modbus_stack_destroy(modbus_stack_t *stack); +/** + * 处理接收到的 Modbus 数据包 + * + * @param stack 协议栈对象 + * @param slave_id 从站 ID + * @param buf 数据缓冲区 + * @return 成功返回数据包大小,失败返回负数 + */ int modbus_stack_recv(modbus_stack_t *stack, uint8_t slave_id, neu_protocol_unpack_buf_t *buf); +/** + * 发送 Modbus 读请求 + * + * @param stack 协议栈对象 + * @param slave_id 从站 ID + * @param area Modbus 区域类型 + * @param start_address 起始地址 + * @param n_reg 寄存器数量 + * @param response_size 返回预期响应大小 + * @return 成功返回发送的字节数,失败返回负数 + */ int modbus_stack_read(modbus_stack_t *stack, uint8_t slave_id, enum modbus_area area, uint16_t start_address, uint16_t n_reg, uint16_t *response_size); + +/** + * 发送 Modbus 写请求 + * + * @param stack 协议栈对象 + * @param req 请求上下文 + * @param slave_id 从站 ID + * @param area Modbus 区域类型 + * @param start_address 起始地址 + * @param n_reg 寄存器数量 + * @param bytes 写入数据 + * @param n_byte 数据字节数 + * @param response_size 返回预期响应大小 + * @param response 是否需要响应 + * @return 成功返回发送的字节数,失败返回负数 + */ int modbus_stack_write(modbus_stack_t *stack, void *req, uint8_t slave_id, enum modbus_area area, uint16_t start_address, uint16_t n_reg, uint8_t *bytes, uint8_t n_byte, uint16_t *response_size, bool response); + +/** + * 判断协议栈是否为 RTU 模式 + * + * @param stack 协议栈对象 + * @return 如果是 RTU 模式则返回 true,否则返回 false + */ bool modbus_stack_is_rtu(modbus_stack_t *stack); #endif \ No newline at end of file diff --git a/plugins/modbus/modbus_tcp.c b/plugins/modbus/modbus_tcp.c index dfb6d3c99..321ea1e2f 100644 --- a/plugins/modbus/modbus_tcp.c +++ b/plugins/modbus/modbus_tcp.c @@ -26,22 +26,118 @@ #include "modbus_req.h" #include "modbus_stack.h" +/** + * modbus_tcp.c + * 本文件实现了 Modbus TCP 协议插件的驱动接口,包括: + * - 初始化和配置 + * - 启动和停止 + * - 标签校验 + * - 分组定时器处理 + * - 写入操作 + * 支持 TCP 和 UDP 通信方式,使用 Modbus TCP 协议格式。 + */ + +/** + * 创建驱动插件 + * @return 插件对象指针 + */ static neu_plugin_t *driver_open(void); +/** + * 关闭驱动插件 + * @param plugin 插件对象 + * @return 处理结果 + */ static int driver_close(neu_plugin_t *plugin); + +/** + * 初始化驱动插件 + * @param plugin 插件对象 + * @param load 是否加载配置 + * @return 处理结果 + */ static int driver_init(neu_plugin_t *plugin, bool load); + +/** + * 反初始化驱动插件 + * @param plugin 插件对象 + * @return 处理结果 + */ static int driver_uninit(neu_plugin_t *plugin); + +/** + * 启动驱动插件 + * @param plugin 插件对象 + * @return 处理结果 + */ static int driver_start(neu_plugin_t *plugin); + +/** + * 停止驱动插件 + * @param plugin 插件对象 + * @return 处理结果 + */ static int driver_stop(neu_plugin_t *plugin); + +/** + * 配置驱动插件 + * @param plugin 插件对象 + * @param config 配置字符串 + * @return 处理结果 + */ static int driver_config(neu_plugin_t *plugin, const char *config); + +/** + * 处理请求 + * @param plugin 插件对象 + * @param head 请求头 + * @param data 请求数据 + * @return 处理结果 + */ static int driver_request(neu_plugin_t *plugin, neu_reqresp_head_t *head, void *data); +/** + * 验证标签格式 + * @param tag 标签 + * @return 验证结果 + */ static int driver_tag_validator(const neu_datatag_t *tag); + +/** + * 验证标签 + * @param plugin 插件对象 + * @param tag 标签 + * @return 验证结果 + */ static int driver_validate_tag(neu_plugin_t *plugin, neu_datatag_t *tag); + +/** + * 分组定时器处理 + * @param plugin 插件对象 + * @param group 分组 + * @return 处理结果 + */ static int driver_group_timer(neu_plugin_t *plugin, neu_plugin_group_t *group); + +/** + * 写入单个标签 + * @param plugin 插件对象 + * @param req 请求上下文 + * @param tag 标签 + * @param value 写入值 + * @return 处理结果 + */ static int driver_write(neu_plugin_t *plugin, void *req, neu_datatag_t *tag, neu_value_u value); + +/** + * 批量写入标签 + * @param plugin 插件对象 + * @param req 请求上下文 + * @param tags 标签数组 + * @return 处理结果 + */ static int driver_write_tags(neu_plugin_t *plugin, void *req, UT_array *tags); static const neu_plugin_intf_funs_t plugin_intf_funs = { @@ -104,8 +200,8 @@ static int driver_init(neu_plugin_t *plugin, bool load) plugin->protocol = MODBUS_PROTOCOL_TCP; plugin->events = neu_event_new(); plugin->stack = modbus_stack_create((void *) plugin, MODBUS_PROTOCOL_TCP, - modbus_send_msg, modbus_value_handle, - modbus_write_resp); + modbus_send_msg, modbus_value_handle, + modbus_write_resp); plog_notice(plugin, "%s init success", plugin->common.name); return 0; @@ -146,20 +242,20 @@ static int driver_stop(neu_plugin_t *plugin) static int driver_config(neu_plugin_t *plugin, const char *config) { int ret = 0; - char * err_param = NULL; + char *err_param = NULL; neu_json_elem_t port = { .name = "port", .t = NEU_JSON_INT }; neu_json_elem_t timeout = { .name = "timeout", .t = NEU_JSON_INT }; neu_json_elem_t host = { .name = "host", - .t = NEU_JSON_STR, - .v.val_str = NULL }; + .t = NEU_JSON_STR, + .v.val_str = NULL }; neu_json_elem_t interval = { .name = "interval", .t = NEU_JSON_INT }; neu_json_elem_t mode = { .name = "connection_mode", .t = NEU_JSON_INT }; neu_conn_param_t param = { 0 }; neu_json_elem_t max_retries = { .name = "max_retries", .t = NEU_JSON_INT }; neu_json_elem_t retry_interval = { .name = "retry_interval", - .t = NEU_JSON_INT }; + .t = NEU_JSON_INT }; neu_json_elem_t endianess_64 = { .name = "endianess_64", - .t = NEU_JSON_INT }; + .t = NEU_JSON_INT }; ret = neu_parse_param((char *) config, &err_param, 5, &port, &host, &mode, &timeout, &interval); diff --git a/plugins/monitor/monitor.c b/plugins/monitor/monitor.c index 8635b3735..fc8646325 100644 --- a/plugins/monitor/monitor.c +++ b/plugins/monitor/monitor.c @@ -17,6 +17,16 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. **/ +/** + * monitor.c - 监控插件主实现文件 + * + * 该文件实现了Neuron系统的监控插件,主要功能包括: + * 1. 通过MQTT协议发送系统监控数据 + * 2. 定时发送心跳消息监测节点状态 + * 3. 处理各类系统事件并发送相应通知 + * 4. 提供系统状态监控和告警功能 + */ + #include #include #include @@ -30,8 +40,17 @@ extern const neu_plugin_module_t neu_plugin_module; +/** + * 监控插件全局实例指针 + * 用于在其他组件中访问监控插件实例 + */ static struct neu_plugin *g_monitor_plugin_; +/** + * MQTT连接成功回调函数 + * + * @param data 插件实例指针 + */ static void connect_cb(void *data) { neu_plugin_t *plugin = data; @@ -39,6 +58,11 @@ static void connect_cb(void *data) plog_notice(plugin, "plugin `%s` connected", neu_plugin_module.module_name); } +/** + * MQTT连接断开回调函数 + * + * @param data 插件实例指针 + */ static void disconnect_cb(void *data) { neu_plugin_t *plugin = data; @@ -47,6 +71,14 @@ static void disconnect_cb(void *data) neu_plugin_module.module_name); } +/** + * 配置MQTT客户端参数 + * + * @param plugin 监控插件实例 + * @param client MQTT客户端实例 + * @param config 监控配置信息 + * @return 0成功,-1失败 + */ static int config_mqtt_client(neu_plugin_t *plugin, neu_mqtt_client_t *client, const monitor_config_t *config) { @@ -56,37 +88,42 @@ static int config_mqtt_client(neu_plugin_t *plugin, neu_mqtt_client_t *client, return 0; } - // set log category as soon as possible to ease debugging + // 尽早设置日志类别以便于调试 rv = neu_mqtt_client_set_zlog_category(client, plugin->common.log); if (0 != rv) { plog_error(plugin, "neu_mqtt_client_set_zlog_category fail"); return -1; } + // 设置MQTT服务器地址和端口 rv = neu_mqtt_client_set_addr(client, config->host, config->port); if (0 != rv) { plog_error(plugin, "neu_mqtt_client_set_host fail"); return -1; } + // 设置MQTT客户端ID rv = neu_mqtt_client_set_id(client, config->client_id); if (0 != rv) { plog_error(plugin, "neu_mqtt_client_set_id fail"); return -1; } + // 设置连接成功回调函数 rv = neu_mqtt_client_set_connect_cb(client, connect_cb, plugin); if (0 != rv) { plog_error(plugin, "neu_mqtt_client_set_connect_cb fail"); return -1; } + // 设置连接断开回调函数 rv = neu_mqtt_client_set_disconnect_cb(client, disconnect_cb, plugin); if (0 != rv) { plog_error(plugin, "neu_mqtt_client_set_disconnect_cb fail"); return -1; } + // 如果配置了用户名,设置认证信息 if (NULL != config->username) { rv = neu_mqtt_client_set_user(client, config->username, config->password); @@ -95,6 +132,7 @@ static int config_mqtt_client(neu_plugin_t *plugin, neu_mqtt_client_t *client, } } + // 设置TLS/SSL加密参数 rv = neu_mqtt_client_set_tls(client, config->ssl, config->ca, config->cert, config->key, config->keypass); if (0 != rv) { @@ -105,12 +143,20 @@ static int config_mqtt_client(neu_plugin_t *plugin, neu_mqtt_client_t *client, return rv; } +/** + * 心跳定时器回调函数 + * + * 定时请求获取所有节点状态信息,用于监控节点健康状况 + * + * @param data 插件实例指针 + * @return 操作结果 + */ static int heartbeat_timer_cb(void *data) { neu_plugin_t *plugin = data; neu_reqresp_head_t header = { - .type = NEU_REQ_GET_NODES_STATE, + .type = NEU_REQ_GET_NODES_STATE, // 请求获取所有节点状态 .ctx = plugin, }; neu_req_get_nodes_state_t cmd = {}; @@ -118,6 +164,11 @@ static int heartbeat_timer_cb(void *data) return neu_plugin_op(plugin, header, &cmd); } +/** + * 停止心跳定时器 + * + * @param plugin 监控插件实例 + */ static inline void stop_heartbeart_timer(neu_plugin_t *plugin) { if (plugin->heartbeat_timer) { @@ -127,15 +178,26 @@ static inline void stop_heartbeart_timer(neu_plugin_t *plugin) } } +/** + * 启动心跳定时器 + * + * 根据配置的时间间隔,定时发送获取节点状态请求 + * + * @param plugin 监控插件实例 + * @param interval 心跳间隔(秒) + * @return 0成功,非零错误码 + */ static int start_hearbeat_timer(neu_plugin_t *plugin, uint64_t interval) { neu_event_timer_t *timer = NULL; + // 如果间隔为0,表示禁用心跳 if (0 == interval) { plog_info(plugin, "heartbeat disabled"); goto end; } + // 如果事件循环不存在,创建一个 if (NULL == plugin->events) { plugin->events = neu_event_new(); if (NULL == plugin->events) { @@ -144,6 +206,7 @@ static int start_hearbeat_timer(neu_plugin_t *plugin, uint64_t interval) } } + // 配置定时器参数 neu_event_timer_param_t param = { .second = interval, .millisecond = 0, @@ -151,6 +214,7 @@ static int start_hearbeat_timer(neu_plugin_t *plugin, uint64_t interval) .usr_data = plugin, }; + // 添加定时器 timer = neu_event_add_timer(plugin->events, param); if (NULL == timer) { plog_error(plugin, "neu_event_add_timer fail"); @@ -158,6 +222,7 @@ static int start_hearbeat_timer(neu_plugin_t *plugin, uint64_t interval) } end: + // 如果已存在定时器,先删除旧的 if (plugin->heartbeat_timer) { neu_event_del_timer(plugin->events, plugin->heartbeat_timer); } @@ -168,21 +233,30 @@ static int start_hearbeat_timer(neu_plugin_t *plugin, uint64_t interval) return 0; } +/** + * 清理定时器和MQTT客户端资源 + * + * @param plugin 监控插件实例 + */ static void uninit_timers_and_mqtt(neu_plugin_t *plugin) { + // 停止心跳定时器 stop_heartbeart_timer(plugin); + // 释放事件循环 if (NULL != plugin->events) { neu_event_close(plugin->events); plugin->events = NULL; } + // 释放配置 if (NULL != plugin->config) { monitor_config_fini(plugin->config); free(plugin->config); plugin->config = NULL; } + // 释放MQTT客户端 if (NULL != plugin->mqtt_client) { neu_mqtt_client_close(plugin->mqtt_client); neu_mqtt_client_free(plugin->mqtt_client); @@ -190,6 +264,13 @@ static void uninit_timers_and_mqtt(neu_plugin_t *plugin) } } +/** + * 监控插件打开函数 + * + * 创建并初始化插件实例 + * + * @return 创建的插件实例指针,失败返回NULL + */ static neu_plugin_t *monitor_plugin_open(void) { neu_plugin_t *plugin = calloc(1, sizeof(neu_plugin_t)); @@ -197,12 +278,22 @@ static neu_plugin_t *monitor_plugin_open(void) return NULL; } + // 初始化插件通用部分 neu_plugin_common_init(&plugin->common); + // 设置全局插件实例指针 g_monitor_plugin_ = plugin; return plugin; } +/** + * 监控插件关闭函数 + * + * 释放插件资源 + * + * @param plugin 监控插件实例 + * @return 操作结果 + */ static int monitor_plugin_close(neu_plugin_t *plugin) { int rv = 0; @@ -214,6 +305,13 @@ static int monitor_plugin_close(neu_plugin_t *plugin) return rv; } +/** + * 监控插件初始化函数 + * + * @param plugin 监控插件实例 + * @param load 是否为加载时初始化 + * @return 操作结果 + */ static int monitor_plugin_init(neu_plugin_t *plugin, bool load) { int rv = 0; @@ -224,10 +322,19 @@ static int monitor_plugin_init(neu_plugin_t *plugin, bool load) return rv; } +/** + * 监控插件反初始化函数 + * + * 清理插件资源 + * + * @param plugin 监控插件实例 + * @return 操作结果 + */ static int monitor_plugin_uninit(neu_plugin_t *plugin) { int rv = 0; + // 清理定时器和MQTT客户端资源 uninit_timers_and_mqtt(plugin); plog_notice(plugin, "Uninitialize plugin: %s", @@ -235,18 +342,29 @@ static int monitor_plugin_uninit(neu_plugin_t *plugin) return rv; } +/** + * 监控插件配置函数 + * + * 解析并应用插件设置 + * + * @param plugin 监控插件实例 + * @param setting JSON格式的配置字符串 + * @return 操作结果,0成功,非零错误码 + */ static int monitor_plugin_config(neu_plugin_t *plugin, const char *setting) { int rv = 0; - const char * plugin_name = neu_plugin_module.module_name; + const char *plugin_name = neu_plugin_module.module_name; monitor_config_t config = { 0 }; + // 解析配置 rv = monitor_config_parse(plugin, setting, &config); if (0 != rv) { plog_error(plugin, "monitor_config_parse fail"); return NEU_ERR_NODE_SETTING_INVALID; } + // 如果配置不存在,分配内存 if (NULL == plugin->config) { plugin->config = calloc(1, sizeof(*plugin->config)); if (NULL == plugin->config) { @@ -256,6 +374,7 @@ static int monitor_plugin_config(neu_plugin_t *plugin, const char *setting) } } + // 如果MQTT客户端不存在,创建一个 if (NULL == plugin->mqtt_client) { plugin->mqtt_client = neu_mqtt_client_new(NEU_MQTT_VERSION_V311); if (NULL == plugin->mqtt_client) { @@ -264,6 +383,7 @@ static int monitor_plugin_config(neu_plugin_t *plugin, const char *setting) goto error; } } else if (neu_mqtt_client_is_open(plugin->mqtt_client)) { + // 如果MQTT客户端已经打开,先关闭它 rv = neu_mqtt_client_close(plugin->mqtt_client); if (0 != rv) { plog_error(plugin, "neu_mqtt_client_close fail"); @@ -272,18 +392,21 @@ static int monitor_plugin_config(neu_plugin_t *plugin, const char *setting) } } + // 配置MQTT客户端 rv = config_mqtt_client(plugin, plugin->mqtt_client, &config); if (0 != rv) { rv = NEU_ERR_MQTT_INIT_FAILURE; goto error; } + // 如果插件已启动,打开MQTT连接 if (plugin->started && 0 != neu_mqtt_client_open(plugin->mqtt_client)) { plog_error(plugin, "neu_mqtt_client_open fail"); rv = NEU_ERR_MQTT_CONNECT_FAILURE; goto error; } + // 如果插件已启动,启动心跳定时器 if (plugin->started && 0 != start_hearbeat_timer(plugin, config.heartbeat_interval)) { plog_error(plugin, "start_hearbeat_timer fail"); @@ -291,9 +414,10 @@ static int monitor_plugin_config(neu_plugin_t *plugin, const char *setting) goto error; } + // 更新配置 monitor_config_fini(plugin->config); memmove(plugin->config, &config, sizeof(config)); - // `config` moved, do not call monitor_config_fini + // 注意:config已移动,不要调用monitor_config_fini plog_notice(plugin, "config plugin `%s` success", plugin_name); return 0; @@ -304,31 +428,44 @@ static int monitor_plugin_config(neu_plugin_t *plugin, const char *setting) return rv; } -static int monitor_plugin_request(neu_plugin_t * plugin, +/** + * 监控插件请求处理函数 + * + * 处理各类系统消息和事件 + * + * @param plugin 监控插件实例 + * @param header 消息头 + * @param data 消息数据 + * @return 操作结果 + */ +static int monitor_plugin_request(neu_plugin_t *plugin, neu_reqresp_head_t *header, void *data) { (void) plugin; switch (header->type) { case NEU_RESP_ERROR: { + // 处理错误响应 neu_resp_error_t *resp_err = (neu_resp_error_t *) data; nlog_warn("recv error code: %d", resp_err->error); break; } case NEU_RESP_GET_NODES_STATE: { + // 处理节点状态响应 handle_nodes_state(header->ctx, (neu_resp_get_nodes_state_t *) data); break; } - case NEU_REQ_ADD_NODE_EVENT: - case NEU_REQ_DEL_NODE_EVENT: - case NEU_REQ_NODE_CTL_EVENT: - case NEU_REQ_NODE_SETTING_EVENT: - case NEU_REQ_ADD_GROUP_EVENT: - case NEU_REQ_DEL_GROUP_EVENT: - case NEU_REQ_UPDATE_GROUP_EVENT: - case NEU_REQ_ADD_TAG_EVENT: - case NEU_REQ_DEL_TAG_EVENT: - case NEU_REQ_UPDATE_TAG_EVENT: { + // 处理各类系统事件 + case NEU_REQ_ADD_NODE_EVENT: // 添加节点事件 + case NEU_REQ_DEL_NODE_EVENT: // 删除节点事件 + case NEU_REQ_NODE_CTL_EVENT: // 节点控制事件 + case NEU_REQ_NODE_SETTING_EVENT: // 节点设置事件 + case NEU_REQ_ADD_GROUP_EVENT: // 添加组事件 + case NEU_REQ_DEL_GROUP_EVENT: // 删除组事件 + case NEU_REQ_UPDATE_GROUP_EVENT: // 更新组事件 + case NEU_REQ_ADD_TAG_EVENT: // 添加标签事件 + case NEU_REQ_DEL_TAG_EVENT: // 删除标签事件 + case NEU_REQ_UPDATE_TAG_EVENT: { // 更新标签事件 handle_events(plugin, header->type, data); break; } @@ -340,22 +477,33 @@ static int monitor_plugin_request(neu_plugin_t * plugin, return 0; } +/** + * 监控插件启动函数 + * + * 打开MQTT连接并启动心跳定时器 + * + * @param plugin 监控插件实例 + * @return 操作结果 + */ static int monitor_plugin_start(neu_plugin_t *plugin) { int rv = 0; const char *plugin_name = neu_plugin_module.module_name; + // 检查MQTT客户端是否存在 if (NULL == plugin->mqtt_client) { plog_notice(plugin, "mqtt client is NULL"); goto end; } + // 打开MQTT连接 if (0 != neu_mqtt_client_open(plugin->mqtt_client)) { plog_error(plugin, "neu_mqtt_client_open fail"); rv = NEU_ERR_MQTT_CONNECT_FAILURE; goto end; } + // 启动心跳定时器 if (0 != start_hearbeat_timer(plugin, plugin->config->heartbeat_interval)) { plog_error(plugin, "start_hearbeat_timer fail"); neu_mqtt_client_close(plugin->mqtt_client); @@ -375,13 +523,23 @@ static int monitor_plugin_start(neu_plugin_t *plugin) return 0; } +/** + * 监控插件停止函数 + * + * 关闭MQTT连接并停止心跳定时器 + * + * @param plugin 监控插件实例 + * @return 操作结果 + */ static int monitor_plugin_stop(neu_plugin_t *plugin) { + // 关闭MQTT连接 if (plugin->mqtt_client) { neu_mqtt_client_close(plugin->mqtt_client); plog_notice(plugin, "mqtt client closed"); } + // 停止心跳定时器 stop_heartbeart_timer(plugin); plugin->started = false; plog_notice(plugin, "stop plugin `%s` success", @@ -389,35 +547,46 @@ static int monitor_plugin_stop(neu_plugin_t *plugin) return 0; } +/** + * 监控插件接口函数表 + */ static const neu_plugin_intf_funs_t plugin_intf_funs = { - .open = monitor_plugin_open, - .close = monitor_plugin_close, - .init = monitor_plugin_init, - .uninit = monitor_plugin_uninit, - .start = monitor_plugin_start, - .stop = monitor_plugin_stop, - .setting = monitor_plugin_config, - .request = monitor_plugin_request, + .open = monitor_plugin_open, // 打开插件 + .close = monitor_plugin_close, // 关闭插件 + .init = monitor_plugin_init, // 初始化插件 + .uninit = monitor_plugin_uninit, // 反初始化插件 + .start = monitor_plugin_start, // 启动插件 + .stop = monitor_plugin_stop, // 停止插件 + .setting = monitor_plugin_config, // 配置插件 + .request = monitor_plugin_request, // 处理请求 }; #define DEFAULT_MONITOR_PLUGIN_DESCR \ "Builtin plugin for Neuron monitoring and alerting" #define DEFAULT_MONITOR_PLUGIN_DESCR_ZH "内置监控与告警插件" +/** + * 监控插件模块定义 + */ const neu_plugin_module_t neu_plugin_module = { - .version = NEURON_PLUGIN_VER_1_0, - .schema = "monitor", - .module_name = "Monitor", - .module_descr = DEFAULT_MONITOR_PLUGIN_DESCR, - .module_descr_zh = DEFAULT_MONITOR_PLUGIN_DESCR_ZH, - .intf_funs = &plugin_intf_funs, - .kind = NEU_PLUGIN_KIND_SYSTEM, - .type = NEU_NA_TYPE_APP, - .display = true, - .single = false, - .single_name = "monitor", + .version = NEURON_PLUGIN_VER_1_0, // 插件版本 + .schema = "monitor", // 插件模式 + .module_name = "Monitor", // 模块名称 + .module_descr = DEFAULT_MONITOR_PLUGIN_DESCR, // 模块描述(英文) + .module_descr_zh = DEFAULT_MONITOR_PLUGIN_DESCR_ZH, // 模块描述(中文) + .intf_funs = &plugin_intf_funs, // 接口函数表 + .kind = NEU_PLUGIN_KIND_SYSTEM, // 插件类型:系统插件 + .type = NEU_NA_TYPE_APP, // 应用类型 + .display = true, // 是否显示 + .single = false, // 是否单例 + .single_name = "monitor", // 单例名称 }; +/** + * 获取监控插件实例 + * + * @return 监控插件实例指针 + */ neu_plugin_t *neu_monitor_get_plugin() { return g_monitor_plugin_; diff --git a/plugins/monitor/monitor_config.c b/plugins/monitor/monitor_config.c index 0bd5f98dc..fa9c5ea59 100644 --- a/plugins/monitor/monitor_config.c +++ b/plugins/monitor/monitor_config.c @@ -17,6 +17,16 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. **/ +/** + * monitor_config.c - 监控插件配置处理 + * + * 该文件实现了监控插件的配置解析和管理功能,包括: + * 1. 解析JSON格式的插件配置 + * 2. 处理MQTT连接参数和TLS/SSL配置 + * 3. 验证配置参数的有效性 + * 4. 提供配置的初始化和清理功能 + */ + #include "json/json.h" #include "json/neu_json_param.h" @@ -27,37 +37,57 @@ #include "monitor.h" #include "monitor_config.h" -#define MB 1000000 +#define MB 1000000 // 1MB大小定义 +/** + * 解码Base64参数 + * + * @param plugin 插件实例 + * @param el JSON参数元素,包含Base64编码的字符串 + * @return 成功返回0,失败返回-1 + */ static inline int decode_b64_param(neu_plugin_t *plugin, neu_json_elem_t *el) { int len = 0; char *s = (char *) neu_decode64(&len, el->v.val_str); + // 检查解码是否成功 if (NULL == s) { plog_error(plugin, "setting %s invalid base64", el->name); return -1; } + // 检查解码后的内容是否为空 if (0 == len) { plog_error(plugin, "setting empty %s", el->name); free(s); return -1; } + // 释放原始字符串,使用解码后的字符串 free(el->v.val_str); el->v.val_str = s; return 0; } +/** + * 解析并解码Base64参数 + * + * @param plugin 插件实例 + * @param setting JSON配置字符串 + * @param el 要填充的JSON参数元素 + * @return 成功返回0,失败返回-1 + */ static inline int parse_b64_param(neu_plugin_t *plugin, const char *setting, neu_json_elem_t *el) { + // 从配置中解析参数 if (0 != neu_parse_param(setting, NULL, 1, el)) { plog_error(plugin, "setting no %s", el->name); return -1; } + // 解码Base64参数 if (0 != decode_b64_param(plugin, el)) { free(el->v.val_str); el->v.val_str = NULL; @@ -67,35 +97,49 @@ static inline int parse_b64_param(neu_plugin_t *plugin, const char *setting, return 0; } +/** + * 解析SSL/TLS参数 + * + * @param plugin 插件实例 + * @param setting JSON配置字符串 + * @param ssl SSL启用标志参数 + * @param ca CA证书参数 + * @param cert 客户端证书参数 + * @param key 客户端私钥参数 + * @param keypass 私钥密码参数 + * @return 成功返回0,失败返回-1 + */ static inline int parse_ssl_params(neu_plugin_t *plugin, const char *setting, neu_json_elem_t *ssl, neu_json_elem_t *ca, neu_json_elem_t *cert, neu_json_elem_t *key, neu_json_elem_t *keypass) { - // ssl, optional + // 解析ssl参数(可选) int ret = neu_parse_param(setting, NULL, 1, ssl); if (0 != ret) { plog_notice(plugin, "setting no ssl"); return 0; } + // 如果SSL被禁用,直接返回 if (false == ssl->v.val_bool) { plog_notice(plugin, "setting ssl disabled"); return 0; } - // ca, optional + // 解析ca证书参数(可选) ret = neu_parse_param(setting, NULL, 1, ca); if (0 != ret) { plog_notice(plugin, "setting no ca"); return 0; } + // 解码CA证书(Base64格式) if (0 != decode_b64_param(plugin, ca)) { return -1; } - // cert, optional + // 解析客户端证书参数(可选) ret = neu_parse_param(setting, NULL, 1, cert); if (0 != ret) { plog_notice(plugin, "setting no cert"); @@ -218,7 +262,7 @@ int monitor_config_parse(neu_plugin_t *plugin, const char *setting, monitor_config_t *config) { int ret = 0; - char * err_param = NULL; + char *err_param = NULL; const char *placeholder = "********"; neu_json_elem_t client_id = { .name = "client-id", .t = NEU_JSON_STR }; @@ -227,7 +271,7 @@ int monitor_config_parse(neu_plugin_t *plugin, const char *setting, neu_json_elem_t heartbeat_interval = { .name = "heartbeat-interval", .t = NEU_JSON_INT }; neu_json_elem_t heartbeat_topic = { .name = "heartbeat-topic", - .t = NEU_JSON_STR }; + .t = NEU_JSON_STR }; neu_json_elem_t host = { .name = "host", .t = NEU_JSON_STR }; neu_json_elem_t port = { .name = "port", .t = NEU_JSON_INT }; neu_json_elem_t username = { .name = "username", .t = NEU_JSON_STR }; diff --git a/plugins/monitor/mqtt_handle.c b/plugins/monitor/mqtt_handle.c index a57e505f1..cbfb0469d 100644 --- a/plugins/monitor/mqtt_handle.c +++ b/plugins/monitor/mqtt_handle.c @@ -17,6 +17,16 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. **/ +/** + * mqtt_handle.c - 监控插件MQTT处理实现 + * + * 该文件实现了监控插件通过MQTT协议发送监控数据和系统事件的功能: + * 1. 处理节点状态变化并通过MQTT发送状态更新 + * 2. 处理系统事件(节点、组、标签的增删改)并通过MQTT发送事件通知 + * 3. 将各类系统信息转换为JSON格式的MQTT消息 + * 4. 管理MQTT消息的主题和发布流程 + */ + #include "connection/mqtt_client.h" #include "errcodes.h" #include "version.h" @@ -30,58 +40,87 @@ #include "monitor.h" #include "mqtt_handle.h" +/** + * 生成节点状态心跳JSON消息 + * + * 将节点状态信息转换为JSON格式的MQTT消息 + * + * @param plugin 监控插件实例 + * @param states 节点状态数组 + * @return 生成的JSON字符串,失败返回NULL + */ static char *generate_heartbeat_json(neu_plugin_t *plugin, UT_array *states) { (void) plugin; - char * version = NEURON_VERSION; + char *version = NEURON_VERSION; + // 创建JSON消息头部,包含版本和时间戳 neu_json_states_head_t header = { .version = version, - .timpstamp = global_timestamp }; + .timpstamp = global_timestamp }; neu_json_states_t json = { 0 }; - char * json_str = NULL; + char *json_str = NULL; + // 分配节点状态数组 json.n_state = utarray_len(states); json.states = calloc(json.n_state, sizeof(neu_json_node_state_t)); if (NULL == json.states) { return NULL; } + // 遍历所有节点状态并填充JSON结构体 utarray_foreach(states, neu_nodes_state_t *, state) { int index = utarray_eltidx(states, state); - json.states[index].node = state->node; - json.states[index].link = state->state.link; - json.states[index].running = state->state.running; + json.states[index].node = state->node; // 节点名称 + json.states[index].link = state->state.link; // 连接状态 + json.states[index].running = state->state.running; // 运行状态 } + // 将结构体编码为JSON字符串 neu_json_encode_with_mqtt(&json, neu_json_encode_states_resp, &header, neu_json_encode_state_header_resp, &json_str); + // 释放临时分配的内存 free(json.states); return json_str; } +/** + * 生成事件JSON消息 + * + * 根据事件类型生成对应的JSON消息内容和MQTT主题 + * + * @param plugin 监控插件实例 + * @param event 事件类型 + * @param data 事件数据 + * @param topic_p 返回的MQTT主题 + * @return 生成的JSON字符串,失败返回NULL + */ static char *generate_event_json(neu_plugin_t *plugin, neu_reqresp_type_e event, void *data, char **topic_p) { char *json_str = NULL; + // 联合体用于不同事件类型的JSON编码 union { - neu_json_add_node_req_t add_node; - neu_json_del_node_req_t del_node; - neu_json_node_ctl_req_t node_ctl; - neu_json_node_setting_req_t node_setting; - neu_json_add_group_config_req_t add_grp; - neu_json_del_group_config_req_t del_grp; - neu_json_update_group_config_req_t update_grp; - neu_json_add_tags_req_t add_tags; - neu_json_del_tags_req_t del_tags; + neu_json_add_node_req_t add_node; // 添加节点 + neu_json_del_node_req_t del_node; // 删除节点 + neu_json_node_ctl_req_t node_ctl; // 节点控制 + neu_json_node_setting_req_t node_setting; // 节点设置 + neu_json_add_group_config_req_t add_grp; // 添加组 + neu_json_del_group_config_req_t del_grp; // 删除组 + neu_json_update_group_config_req_t update_grp; // 更新组 + neu_json_add_tags_req_t add_tags; // 添加标签 + neu_json_del_tags_req_t del_tags; // 删除标签 } json_req = {}; + // 根据事件类型处理不同的事件数据 switch (event) { case NEU_REQ_ADD_NODE_EVENT: { + // 处理添加节点事件 neu_req_add_node_t *add_node = data; - json_req.add_node.name = add_node->node; - json_req.add_node.plugin = add_node->plugin; - *topic_p = plugin->config->node_add_topic; + json_req.add_node.name = add_node->node; // 节点名称 + json_req.add_node.plugin = add_node->plugin; // 插件名称 + *topic_p = plugin->config->node_add_topic; // 设置MQTT主题 + // 编码为JSON字符串 neu_json_encode_by_fn(&json_req, neu_json_encode_add_node_req, &json_str); break; @@ -214,6 +253,18 @@ static char *generate_event_json(neu_plugin_t *plugin, neu_reqresp_type_e event, return json_str; } +/** + * MQTT消息发布回调函数 + * + * 在消息发布完成后更新指标统计 + * + * @param errcode 错误码,0表示成功 + * @param qos 服务质量等级 + * @param topic 消息主题 + * @param payload 消息内容 + * @param len 消息长度 + * @param data 用户数据(插件实例) + */ static void publish_cb(int errcode, neu_mqtt_qos_e qos, char *topic, uint8_t *payload, uint32_t len, void *data) { @@ -223,30 +274,48 @@ static void publish_cb(int errcode, neu_mqtt_qos_e qos, char *topic, neu_plugin_t *plugin = data; + // 获取指标更新回调函数 neu_adapter_update_metric_cb_t update_metric = plugin->common.adapter_callbacks->update_metric; + // 更新消息发送统计指标 if (0 == errcode) { + // 发送成功,增加成功计数 update_metric(plugin->common.adapter, NEU_METRIC_SEND_MSGS_TOTAL, 1, NULL); } else { + // 发送失败,增加错误计数 update_metric(plugin->common.adapter, NEU_METRIC_SEND_MSG_ERRORS_TOTAL, 1, NULL); } + // 释放消息内容内存 free(payload); } +/** + * 发布MQTT消息 + * + * @param plugin 监控插件实例 + * @param qos 服务质量等级 + * @param topic 消息主题 + * @param payload 消息内容 + * @param payload_len 消息长度 + * @return 0成功,非零错误码 + */ static inline int publish(neu_plugin_t *plugin, neu_mqtt_qos_e qos, char *topic, char *payload, size_t payload_len) { + // 获取指标更新回调函数 neu_adapter_update_metric_cb_t update_metric = plugin->common.adapter_callbacks->update_metric; + // 发布MQTT消息 int rv = neu_mqtt_client_publish( plugin->mqtt_client, qos, topic, (uint8_t *) payload, (uint32_t) payload_len, plugin, publish_cb); if (0 != rv) { + // 发布失败,记录错误并更新错误计数 plog_error(plugin, "pub [%s, QoS%d] fail", topic, qos); update_metric(plugin->common.adapter, NEU_METRIC_SEND_MSG_ERRORS_TOTAL, 1, NULL); @@ -257,22 +326,34 @@ static inline int publish(neu_plugin_t *plugin, neu_mqtt_qos_e qos, char *topic, return rv; } +/** + * 处理节点状态信息 + * + * 将节点状态信息转换为JSON格式并通过MQTT发送 + * + * @param plugin 监控插件实例 + * @param states 节点状态信息 + * @return 0成功,非零错误码 + */ int handle_nodes_state(neu_plugin_t *plugin, neu_reqresp_nodes_state_t *states) { int rv = 0; char *json_str = NULL; + // 检查MQTT客户端是否存在 if (NULL == plugin->mqtt_client) { rv = NEU_ERR_MQTT_IS_NULL; goto end; } + // 检查MQTT连接状态 if (!neu_mqtt_client_is_connected(plugin->mqtt_client)) { - // cache disable and we are disconnected + // 缓存被禁用且我们已断开连接 rv = NEU_ERR_MQTT_FAILURE; goto end; } + // 生成节点状态JSON消息 json_str = generate_heartbeat_json(plugin, states->states); if (NULL == json_str) { plog_error(plugin, "generate heartbeat json fail"); @@ -280,7 +361,7 @@ int handle_nodes_state(neu_plugin_t *plugin, neu_reqresp_nodes_state_t *states) goto end; } - char * topic = plugin->config->heartbeat_topic; + char *topic = plugin->config->heartbeat_topic; neu_mqtt_qos_e qos = NEU_MQTT_QOS0; rv = publish(plugin, qos, topic, json_str, strlen(json_str)); json_str = NULL; @@ -291,23 +372,36 @@ int handle_nodes_state(neu_plugin_t *plugin, neu_reqresp_nodes_state_t *states) return rv; } +/** + * 处理系统事件 + * + * 将系统事件转换为JSON格式并通过MQTT发送 + * + * @param plugin 监控插件实例 + * @param event 事件类型 + * @param data 事件数据 + * @return 0成功,非零错误码 + */ int handle_events(neu_plugin_t *plugin, neu_reqresp_type_e event, void *data) { int rv = 0; char *json_str = NULL; char *topic = NULL; + // 检查MQTT客户端是否存在 if (NULL == plugin->mqtt_client) { rv = NEU_ERR_MQTT_IS_NULL; goto end; } + // 检查MQTT连接状态 if (!neu_mqtt_client_is_connected(plugin->mqtt_client)) { - // cache disable and we are disconnected + // 缓存被禁用且我们已断开连接 rv = NEU_ERR_MQTT_FAILURE; goto end; } + // 生成事件JSON消息和对应的主题 json_str = generate_event_json(plugin, event, data, &topic); if (NULL == json_str) { plog_error(plugin, "generate event:%s json fail", @@ -316,17 +410,22 @@ int handle_events(neu_plugin_t *plugin, neu_reqresp_type_e event, void *data) goto end; } - neu_mqtt_qos_e qos = NEU_MQTT_QOS0; + // 发布MQTT消息 + neu_mqtt_qos_e qos = NEU_MQTT_QOS0; // 使用QoS0服务质量 rv = publish(plugin, qos, topic, json_str, strlen(json_str)); json_str = NULL; end: + // 根据事件类型清理资源 if (NEU_REQ_NODE_SETTING_EVENT == event) { + // 清理节点设置事件资源 neu_req_node_setting_fini(data); } else if (NEU_REQ_ADD_TAG_EVENT == event || NEU_REQ_UPDATE_TAG_EVENT == event) { + // 清理添加/更新标签事件资源 neu_req_add_tag_fini(data); } else if (NEU_REQ_DEL_TAG_EVENT == event) { + // 清理删除标签事件资源 neu_req_del_tag_fini(data); } return rv; diff --git a/plugins/mqtt/mqtt_config.c b/plugins/mqtt/mqtt_config.c index 5a80caa62..feeef4a46 100644 --- a/plugins/mqtt/mqtt_config.c +++ b/plugins/mqtt/mqtt_config.c @@ -17,6 +17,16 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. **/ +/** + * mqtt_config.c - MQTT插件配置处理 + * + * 该文件实现了MQTT插件的配置解析和管理功能,包括: + * 1. 解析JSON格式的插件配置 + * 2. 处理MQTT连接参数和TLS/SSL配置 + * 3. 验证配置参数的有效性 + * 4. 提供配置的初始化和清理功能 + */ + #include "utils/asprintf.h" #include "json/json.h" #include "json/neu_json_param.h" @@ -24,37 +34,57 @@ #include "mqtt_config.h" #include "mqtt_plugin.h" -#define MB 1000000 +#define MB 1000000 // 1MB大小定义 +/** + * 解码Base64参数 + * + * @param plugin 插件实例 + * @param el JSON参数元素,包含Base64编码的字符串 + * @return 成功返回0,失败返回-1 + */ static inline int decode_b64_param(neu_plugin_t *plugin, neu_json_elem_t *el) { int len = 0; char *s = (char *) neu_decode64(&len, el->v.val_str); + // 检查解码是否成功 if (NULL == s) { plog_error(plugin, "setting %s invalid base64", el->name); return -1; } + // 检查解码后的内容是否为空 if (0 == len) { plog_error(plugin, "setting empty %s", el->name); free(s); return -1; } + // 释放原始字符串,使用解码后的字符串 free(el->v.val_str); el->v.val_str = s; return 0; } +/** + * 解析并解码Base64参数 + * + * @param plugin 插件实例 + * @param setting JSON配置字符串 + * @param el 要填充的JSON参数元素 + * @return 成功返回0,失败返回-1 + */ static inline int parse_b64_param(neu_plugin_t *plugin, const char *setting, neu_json_elem_t *el) { + // 从配置中解析参数 if (0 != neu_parse_param(setting, NULL, 1, el)) { plog_error(plugin, "setting no %s", el->name); return -1; } + // 解码Base64参数 if (0 != decode_b64_param(plugin, el)) { free(el->v.val_str); el->v.val_str = NULL; @@ -64,53 +94,68 @@ static inline int parse_b64_param(neu_plugin_t *plugin, const char *setting, return 0; } +/** + * 解析SSL/TLS参数 + * + * @param plugin 插件实例 + * @param setting JSON配置字符串 + * @param ssl SSL启用标志参数 + * @param ca CA证书参数 + * @param cert 客户端证书参数 + * @param key 客户端私钥参数 + * @param keypass 私钥密码参数 + * @return 成功返回0,失败返回-1 + */ static inline int parse_ssl_params(neu_plugin_t *plugin, const char *setting, neu_json_elem_t *ssl, neu_json_elem_t *ca, neu_json_elem_t *cert, neu_json_elem_t *key, neu_json_elem_t *keypass) { - // ssl, optional + // 解析ssl参数(可选) int ret = neu_parse_param(setting, NULL, 1, ssl); if (0 != ret) { plog_notice(plugin, "setting no ssl"); return 0; } + // 如果SSL被禁用,直接返回 if (false == ssl->v.val_bool) { plog_notice(plugin, "setting ssl disabled"); return 0; } - // ca, optional + // 解析ca证书参数(可选) ret = neu_parse_param(setting, NULL, 1, ca); if (0 != ret) { plog_notice(plugin, "setting no ca"); return 0; } + // 解码CA证书(Base64格式) if (0 != decode_b64_param(plugin, ca)) { return -1; } - // cert, optional + // 解析客户端证书参数(可选) ret = neu_parse_param(setting, NULL, 1, cert); if (0 != ret) { plog_notice(plugin, "setting no cert"); return 0; } + // 解码客户端证书(Base64格式) if (0 != decode_b64_param(plugin, cert)) { return -1; } - // key, required if cert enable + // 解析客户端私钥参数(如果启用了证书,则必需) ret = parse_b64_param(plugin, setting, key); if (0 != ret) { return -1; } - // keypass, optional - // keep it here for backward compatibility with version 2.5 + // 解析私钥密码参数(可选) + // 保留此代码以与2.5版本保持向后兼容 ret = neu_parse_param(setting, NULL, 1, keypass); if (0 != ret) { plog_notice(plugin, "setting no keypass"); @@ -122,6 +167,17 @@ static inline int parse_ssl_params(neu_plugin_t *plugin, const char *setting, return 0; } +/** + * 解析缓存相关参数 + * + * @param plugin 插件实例 + * @param setting JSON配置字符串 + * @param offline_cache 离线缓存启用标志 + * @param cache_mem_size 内存缓存大小参数 + * @param cache_disk_size 磁盘缓存大小参数 + * @param cache_sync_interval 缓存同步间隔参数 + * @return 成功返回0,失败返回非0错误码 + */ static int parse_cache_params(neu_plugin_t *plugin, const char *setting, neu_json_elem_t *offline_cache, neu_json_elem_t *cache_mem_size, @@ -130,29 +186,30 @@ static int parse_cache_params(neu_plugin_t *plugin, const char *setting, { int ret = 0; char *err_param = NULL; - bool flag_present = true; // whether setting has `offline-cache` + bool flag_present = true; // 配置中是否存在`offline-cache`标志 - // offline-cache flag, optional + // 解析离线缓存标志(可选) ret = neu_parse_param(setting, NULL, 1, offline_cache); if (0 != ret) { plog_notice(plugin, "setting no offline cache flag"); - flag_present = false; // `offline-cache` not present in setting + flag_present = false; // 配置中不存在`offline-cache`标志 } if (flag_present && !offline_cache->v.val_bool) { - // cache explicitly disabled in setting + // 配置中明确禁用了缓存 cache_mem_size->v.val_int = 0; cache_disk_size->v.val_int = 0; cache_sync_interval->v.val_int = NEU_MQTT_CACHE_SYNC_INTERVAL_DEFAULT; return 0; } - // we are here because one of the following: - // 1. `offline-cache` presents to be true in setting - // 2. no `offline-cache` in setting - // we require `cache_mem_size` and `cache_disk_size` to present in both - // cases, especially in case 2 for backward compatibility. + // 我们来到这里是因为以下其中一种情况: + // 1. 配置中`offline-cache`设置为true + // 2. 配置中没有`offline-cache` + // 在这两种情况下,我们都需要`cache_mem_size`和`cache_disk_size`参数, + // 特别是在第2种情况下,为了向后兼容性。 + // 解析内存缓存和磁盘缓存大小参数 ret = neu_parse_param(setting, &err_param, 2, cache_mem_size, cache_disk_size); if (0 != ret) { @@ -161,6 +218,7 @@ static int parse_cache_params(neu_plugin_t *plugin, const char *setting, return -1; } + // 验证内存缓存大小,范围为1-1024MB if (cache_mem_size->v.val_int > 1024 || (flag_present && cache_mem_size->v.val_int < 1)) { plog_error(plugin, "setting invalid cache memory size: %" PRIi64, @@ -168,6 +226,7 @@ static int parse_cache_params(neu_plugin_t *plugin, const char *setting, return -1; } + // 验证磁盘缓存大小,范围为1-10240MB if (cache_disk_size->v.val_int > 10240 || (flag_present && cache_disk_size->v.val_int < 1)) { plog_error(plugin, "setting invalid cache disk size: %" PRIi64, @@ -175,6 +234,7 @@ static int parse_cache_params(neu_plugin_t *plugin, const char *setting, return -1; } + // 确保内存缓存大小不大于磁盘缓存大小 if (cache_mem_size->v.val_int > cache_disk_size->v.val_int) { plog_error(plugin, "setting cache memory size %" PRIi64 @@ -183,6 +243,7 @@ static int parse_cache_params(neu_plugin_t *plugin, const char *setting, return -1; } + // 不允许只有磁盘缓存而没有内存缓存 if (0 == cache_mem_size->v.val_int && 0 != cache_disk_size->v.val_int) { plog_error(plugin, "setting cache disk size %" PRIi64 " without memory cache", @@ -190,13 +251,15 @@ static int parse_cache_params(neu_plugin_t *plugin, const char *setting, return -1; } + // 如果配置中没有离线缓存标志,根据内存缓存大小来决定是否启用 if (!flag_present) { offline_cache->v.val_bool = cache_mem_size->v.val_int > 0; } - // cache-sync-interval, optional for backward compatibility + // 解析缓存同步间隔参数(为了向后兼容性,此参数是可选的) ret = neu_parse_param(setting, NULL, 1, cache_sync_interval); if (0 == ret) { + // 验证缓存同步间隔是否在有效范围内 if (cache_sync_interval->v.val_int < NEU_MQTT_CACHE_SYNC_INTERVAL_MIN || NEU_MQTT_CACHE_SYNC_INTERVAL_MAX < cache_sync_interval->v.val_int) { plog_error(plugin, "setting invalid cache sync interval: %" PRIi64, @@ -204,6 +267,7 @@ static int parse_cache_params(neu_plugin_t *plugin, const char *setting, return -1; } } else { + // 如果没有配置,使用默认值 plog_notice(plugin, "setting no cache sync interval"); cache_sync_interval->v.val_int = NEU_MQTT_CACHE_SYNC_INTERVAL_DEFAULT; } @@ -211,26 +275,35 @@ static int parse_cache_params(neu_plugin_t *plugin, const char *setting, return 0; } +/** + * 解析MQTT插件配置 + * + * @param plugin 插件实例 + * @param setting JSON配置字符串 + * @param config 配置结构体指针,用于存储解析结果 + * @return 成功返回0,失败返回非0错误码 + */ int mqtt_config_parse(neu_plugin_t *plugin, const char *setting, mqtt_config_t *config) { int ret = 0; - char * err_param = NULL; - const char *placeholder = "********"; + char *err_param = NULL; + const char *placeholder = "********"; // 用于密码等敏感信息的占位符 + // 定义各配置参数 neu_json_elem_t client_id = { .name = "client-id", .t = NEU_JSON_STR }; neu_json_elem_t qos = { - .name = "qos", - .t = NEU_JSON_INT, - .v.val_int = NEU_MQTT_QOS0, // default to QoS0 - .attribute = NEU_JSON_ATTRIBUTE_OPTIONAL, // for backward compatibility + .name = "qos", + .t = NEU_JSON_INT, + .v.val_int = NEU_MQTT_QOS0, // 默认使用QoS0 + .attribute = NEU_JSON_ATTRIBUTE_OPTIONAL, // 为了向后兼容性,此参数可选 }; neu_json_elem_t format = { .name = "format", .t = NEU_JSON_INT }; neu_json_elem_t write_req_topic = { .name = "write-req-topic", .t = NEU_JSON_STR, .v.val_str = NULL, - .attribute = NEU_JSON_ATTRIBUTE_OPTIONAL, // for backward compatibility + .attribute = NEU_JSON_ATTRIBUTE_OPTIONAL, // 为了向后兼容性,此参数可选 }; neu_json_elem_t write_resp_topic = { .name = "write-resp-topic", @@ -238,29 +311,36 @@ int mqtt_config_parse(neu_plugin_t *plugin, const char *setting, .v.val_str = NULL, .attribute = NEU_JSON_ATTRIBUTE_OPTIONAL, // for backward compatibility }; + // 缓存相关参数 neu_json_elem_t offline_cache = { .name = "offline-cache", - .t = NEU_JSON_BOOL }; + .t = NEU_JSON_BOOL }; neu_json_elem_t cache_mem_size = { .name = "cache-mem-size", - .t = NEU_JSON_INT }; + .t = NEU_JSON_INT }; neu_json_elem_t cache_disk_size = { .name = "cache-disk-size", - .t = NEU_JSON_INT }; + .t = NEU_JSON_INT }; neu_json_elem_t cache_sync_interval = { .name = "cache-sync-interval", .t = NEU_JSON_INT }; - neu_json_elem_t host = { .name = "host", .t = NEU_JSON_STR }; - neu_json_elem_t port = { .name = "port", .t = NEU_JSON_INT }; + + // 连接参数 + neu_json_elem_t host = { .name = "host", .t = NEU_JSON_STR }; + neu_json_elem_t port = { .name = "port", .t = NEU_JSON_INT }; neu_json_elem_t username = { .name = "username", .t = NEU_JSON_STR }; neu_json_elem_t password = { .name = "password", .t = NEU_JSON_STR }; - neu_json_elem_t ssl = { .name = "ssl", .t = NEU_JSON_BOOL }; - neu_json_elem_t ca = { .name = "ca", .t = NEU_JSON_STR }; - neu_json_elem_t cert = { .name = "cert", .t = NEU_JSON_STR }; - neu_json_elem_t key = { .name = "key", .t = NEU_JSON_STR }; - neu_json_elem_t keypass = { .name = "keypass", .t = NEU_JSON_STR }; + // SSL/TLS参数 + neu_json_elem_t ssl = { .name = "ssl", .t = NEU_JSON_BOOL }; + neu_json_elem_t ca = { .name = "ca", .t = NEU_JSON_STR }; + neu_json_elem_t cert = { .name = "cert", .t = NEU_JSON_STR }; + neu_json_elem_t key = { .name = "key", .t = NEU_JSON_STR }; + neu_json_elem_t keypass = { .name = "keypass", .t = NEU_JSON_STR }; + + // 验证参数 if (NULL == setting || NULL == config) { plog_error(plugin, "invalid argument, null pointer"); return -1; } + // 解析基本参数 ret = neu_parse_param(setting, &err_param, 7, &client_id, &qos, &format, &write_req_topic, &write_resp_topic, &host, &port); if (0 != ret) { @@ -268,19 +348,19 @@ int mqtt_config_parse(neu_plugin_t *plugin, const char *setting, goto error; } - // client-id, required + // 验证客户端ID(必需) if (0 == strlen(client_id.v.val_str)) { plog_error(plugin, "setting empty client-id"); goto error; } - // qos, optional, default to QoS0 + // 验证QoS参数值范围(可选,默认为QoS0) if (qos.v.val_int < NEU_MQTT_QOS0 || NEU_MQTT_QOS2 < qos.v.val_int) { plog_error(plugin, "setting invalid qos: %" PRIi64, qos.v.val_int); goto error; } - // format, required + // 验证数据格式参数(必需) if (MQTT_UPLOAD_FORMAT_VALUES != format.v.val_int && MQTT_UPLOAD_FORMAT_TAGS != format.v.val_int) { plog_error(plugin, "setting invalid format: %" PRIi64, @@ -288,7 +368,7 @@ int mqtt_config_parse(neu_plugin_t *plugin, const char *setting, goto error; } - // write request topic + // 处理写请求主题(如果未指定,生成默认主题) if (NULL == write_req_topic.v.val_str && 0 > neu_asprintf(&write_req_topic.v.val_str, "/neuron/%s/write/req", plugin->common.name)) { @@ -296,7 +376,7 @@ int mqtt_config_parse(neu_plugin_t *plugin, const char *setting, goto error; } - // write response topic + // 处理写响应主题(如果未指定,生成默认主题) if (NULL == write_resp_topic.v.val_str && 0 > neu_asprintf(&write_resp_topic.v.val_str, "/neuron/%s/write/resp", plugin->common.name)) { @@ -304,61 +384,66 @@ int mqtt_config_parse(neu_plugin_t *plugin, const char *setting, goto error; } - // offline cache + // 解析离线缓存参数 ret = parse_cache_params(plugin, setting, &offline_cache, &cache_mem_size, &cache_disk_size, &cache_sync_interval); if (0 != ret) { goto error; } - // host, required + // 验证主机地址(必需) if (0 == strlen(host.v.val_str)) { plog_error(plugin, "setting invalid host: `%s`", host.v.val_str); goto error; } - // port, required + // 验证端口号(必需且在有效范围内) if (0 == port.v.val_int || port.v.val_int > 65535) { plog_error(plugin, "setting invalid port: %" PRIi64, port.v.val_int); goto error; } - // username, optional + // 解析用户名参数(可选) ret = neu_parse_param(setting, NULL, 1, &username); if (0 != ret) { plog_notice(plugin, "setting no username"); } - // password, optional + // 解析密码参数(可选) ret = neu_parse_param(setting, NULL, 1, &password); if (0 != ret) { plog_notice(plugin, "setting no password"); } + // 解析SSL/TLS相关参数 ret = parse_ssl_params(plugin, setting, &ssl, &ca, &cert, &key, &keypass); if (0 != ret) { goto error; } - config->client_id = client_id.v.val_str; - config->qos = qos.v.val_int; - config->format = format.v.val_int; - config->write_req_topic = write_req_topic.v.val_str; - config->write_resp_topic = write_resp_topic.v.val_str; - config->cache = offline_cache.v.val_bool; - config->cache_mem_size = cache_mem_size.v.val_int * MB; - config->cache_disk_size = cache_disk_size.v.val_int * MB; - config->cache_sync_interval = cache_sync_interval.v.val_int; - config->host = host.v.val_str; - config->port = port.v.val_int; - config->username = username.v.val_str; - config->password = password.v.val_str; - config->ssl = ssl.v.val_bool; - config->ca = ca.v.val_str; - config->cert = cert.v.val_str; - config->key = key.v.val_str; - config->keypass = keypass.v.val_str; - + // 填充配置结构体 + config->client_id = client_id.v.val_str; // 客户端ID + config->qos = qos.v.val_int; // 服务质量 + config->format = format.v.val_int; // 数据格式 + config->write_req_topic = write_req_topic.v.val_str; // 写请求主题 + config->write_resp_topic = write_resp_topic.v.val_str; // 写响应主题 + config->cache = offline_cache.v.val_bool; // 离线缓存开关 + config->cache_mem_size = + cache_mem_size.v.val_int * MB; // 内存缓存大小(MB转字节) + config->cache_disk_size = + cache_disk_size.v.val_int * MB; // 磁盘缓存大小(MB转字节) + config->cache_sync_interval = cache_sync_interval.v.val_int; // 缓存同步间隔 + config->host = host.v.val_str; // 服务器地址 + config->port = port.v.val_int; // 服务器端口 + config->username = username.v.val_str; // 用户名 + config->password = password.v.val_str; // 密码 + config->ssl = ssl.v.val_bool; // SSL/TLS开关 + config->ca = ca.v.val_str; // CA证书 + config->cert = cert.v.val_str; // 客户端证书 + config->key = key.v.val_str; // 客户端私钥 + config->keypass = keypass.v.val_str; // 私钥密码 + + // 记录配置信息到日志 plog_notice(plugin, "config client-id : %s", config->client_id); plog_notice(plugin, "config qos : %d", config->qos); plog_notice(plugin, "config format : %s", @@ -375,15 +460,19 @@ int mqtt_config_parse(neu_plugin_t *plugin, const char *setting, plog_notice(plugin, "config host : %s", config->host); plog_notice(plugin, "config port : %" PRIu16, config->port); + // 记录认证信息(如果有) if (config->username) { plog_notice(plugin, "config username : %s", config->username); } if (config->password) { + // 密码使用占位符保护,不直接输出到日志 plog_notice(plugin, "config password : %s", 0 == strlen(config->password) ? "" : placeholder); } + // 记录SSL/TLS配置 plog_notice(plugin, "config ssl : %d", config->ssl); + // 证书和密钥信息使用占位符保护,不输出实际内容到日志 if (config->ca) { plog_notice(plugin, "config ca : %s", placeholder); } @@ -400,6 +489,7 @@ int mqtt_config_parse(neu_plugin_t *plugin, const char *setting, return 0; error: + // 错误处理:释放已分配的内存 free(err_param); free(client_id.v.val_str); free(write_req_topic.v.val_str); @@ -414,8 +504,14 @@ int mqtt_config_parse(neu_plugin_t *plugin, const char *setting, return -1; } +/** + * 释放MQTT配置结构体资源 + * + * @param config 配置结构体指针 + */ void mqtt_config_fini(mqtt_config_t *config) { + // 释放所有分配的字符串资源 free(config->client_id); free(config->write_req_topic); free(config->write_resp_topic); @@ -427,5 +523,6 @@ void mqtt_config_fini(mqtt_config_t *config) free(config->key); free(config->keypass); + // 清空配置结构体 memset(config, 0, sizeof(*config)); } diff --git a/plugins/mqtt/mqtt_handle.c b/plugins/mqtt/mqtt_handle.c index 20b4cb1c1..39f9c6fb3 100644 --- a/plugins/mqtt/mqtt_handle.c +++ b/plugins/mqtt/mqtt_handle.c @@ -17,6 +17,16 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. **/ +/** + * mqtt_handle.c - MQTT插件消息处理实现 + * + * 该文件实现了MQTT插件的数据处理和消息发布功能,包括: + * 1. 将标签值转换为不同格式的JSON消息 + * 2. 处理和发布数据点 + * 3. 处理订阅的写请求 + * 4. 管理MQTT消息的发布和应答 + */ + #include "connection/mqtt_client.h" #include "errcodes.h" #include "utils/asprintf.h" @@ -27,14 +37,23 @@ #include "mqtt_handle.h" #include "mqtt_plugin.h" +/** + * 将标签值转换为JSON格式 + * + * @param tags 标签数组 + * @param json 要填充的JSON结构 + * @return 成功返回0,失败返回非0值 + */ static int tag_values_to_json(UT_array *tags, neu_json_read_resp_t *json) { int index = 0; + // 如果标签数组为空,直接返回成功 if (0 == utarray_len(tags)) { return 0; } + // 分配JSON标签数组内存 json->n_tag = utarray_len(tags); json->tags = (neu_json_read_resp_tag_t *) calloc( json->n_tag, sizeof(neu_json_read_resp_tag_t)); @@ -42,6 +61,7 @@ static int tag_values_to_json(UT_array *tags, neu_json_read_resp_t *json) return -1; } + // 遍历标签数组,将每个标签值转换为JSON格式 utarray_foreach(tags, neu_resp_tag_value_meta_t *, tag_value) { neu_tag_value_to_json(tag_value, &json->tags[index]); @@ -51,26 +71,36 @@ static int tag_values_to_json(UT_array *tags, neu_json_read_resp_t *json) return 0; } -static char *generate_upload_json(neu_plugin_t * plugin, +/** + * 生成上传数据的JSON字符串 + * + * @param plugin 插件实例 + * @param data 要上传的数据 + * @param format 上传格式(值或标签格式) + * @return 生成的JSON字符串,失败返回NULL + */ +static char *generate_upload_json(neu_plugin_t *plugin, neu_reqresp_trans_data_t *data, mqtt_upload_format_e format) { - char * json_str = NULL; - neu_json_read_periodic_t header = { .group = (char *) data->group, + char *json_str = NULL; + // 创建JSON头部,包含组名、节点名和时间戳 + neu_json_read_periodic_t header = { .group = (char *) data->group, .node = (char *) data->driver, .timestamp = global_timestamp }; - neu_json_read_resp_t json = { 0 }; + neu_json_read_resp_t json = { 0 }; + // 将标签值转换为JSON格式 if (0 != tag_values_to_json(data->tags, &json)) { plog_error(plugin, "tag_values_to_json fail"); return NULL; } - if (MQTT_UPLOAD_FORMAT_VALUES == format) { // values + if (MQTT_UPLOAD_FORMAT_VALUES == format) { // 值格式(简化格式) neu_json_encode_with_mqtt(&json, neu_json_encode_read_resp1, &header, neu_json_encode_read_periodic_resp, &json_str); - } else if (MQTT_UPLOAD_FORMAT_TAGS == format) { // tags + } else if (MQTT_UPLOAD_FORMAT_TAGS == format) { // 标签格式(完整格式) neu_json_encode_with_mqtt(&json, neu_json_encode_read_resp, &header, neu_json_encode_read_periodic_resp, &json_str); @@ -78,6 +108,7 @@ static char *generate_upload_json(neu_plugin_t * plugin, plog_warn(plugin, "invalid upload format: %d", format); } + // 释放已分配的内存 for (int i = 0; i < json.n_tag; i++) { if (json.tags[i].n_meta > 0) { free(json.tags[i].metas); @@ -90,59 +121,90 @@ static char *generate_upload_json(neu_plugin_t * plugin, return json_str; } -static char *generate_read_resp_json(neu_plugin_t * plugin, - neu_json_mqtt_t * mqtt, +/** + * 生成读取响应的JSON字符串 + * + * @param plugin 插件实例 + * @param mqtt MQTT消息结构 + * @param data 读取组数据 + * @return 生成的JSON字符串,失败返回NULL + */ +static char *generate_read_resp_json(neu_plugin_t *plugin, + neu_json_mqtt_t *mqtt, neu_resp_read_group_t *data) { - // neu_resp_tag_value_meta_t *tags = data->tags; - // uint16_t len = data->n_tag; - char * json_str = NULL; + char *json_str = NULL; neu_json_read_resp_t json = { 0 }; + // 将标签值转换为JSON格式 if (0 != tag_values_to_json(data->tags, &json)) { plog_error(plugin, "tag_values_to_json fail"); return NULL; } + // 生成包含MQTT消息的JSON字符串 neu_json_encode_with_mqtt(&json, neu_json_encode_read_resp, mqtt, neu_json_encode_mqtt_resp, &json_str); + // 释放临时分配的内存 if (json.tags) { free(json.tags); } return json_str; } -static char *generate_write_resp_json(neu_plugin_t * plugin, - neu_json_mqtt_t * mqtt, +/** + * 生成写入响应的JSON字符串 + * + * @param plugin 插件实例 + * @param mqtt MQTT消息结构 + * @param data 错误数据 + * @return 生成的JSON字符串,失败返回NULL + */ +static char *generate_write_resp_json(neu_plugin_t *plugin, + neu_json_mqtt_t *mqtt, neu_resp_error_t *data) { (void) plugin; + // 创建错误响应结构 neu_json_error_resp_t error = { .error = data->error }; - char * json_str = NULL; + char *json_str = NULL; + // 生成包含MQTT消息的JSON字符串 neu_json_encode_with_mqtt(&error, neu_json_encode_error_resp, mqtt, neu_json_encode_mqtt_resp, &json_str); return json_str; } +/** + * 发送读取请求 + * + * @param plugin 插件实例 + * @param mqtt MQTT消息结构 + * @param req 读取请求参数 + * @return 操作结果,成功返回0,失败返回非0值 + */ static inline int send_read_req(neu_plugin_t *plugin, neu_json_mqtt_t *mqtt, neu_json_read_req_t *req) { plog_notice(plugin, "read uuid:%s, group:%s, node:%s", mqtt->uuid, req->group, req->node); + // 准备请求头和命令结构 neu_reqresp_head_t header = { 0 }; header.ctx = mqtt; header.type = NEU_REQ_READ_GROUP; neu_req_read_group_t cmd = { 0 }; - cmd.driver = req->node; - cmd.group = req->group; - cmd.sync = req->sync; - req->node = NULL; // ownership moved - req->group = NULL; // ownership moved + // 填充读取组命令结构 + cmd.driver = req->node; // 驱动节点名 + cmd.group = req->group; // 组名 + cmd.sync = req->sync; // 同步标志 + req->node = NULL; // 所有权转移 + req->group = NULL; // 所有权转移 + + // 执行请求操作 if (0 != neu_plugin_op(plugin, header, &cmd)) { neu_req_read_group_fini(&cmd); plog_error(plugin, "neu_plugin_op(NEU_REQ_READ_GROUP) fail"); @@ -152,27 +214,38 @@ static inline int send_read_req(neu_plugin_t *plugin, neu_json_mqtt_t *mqtt, return 0; } +/** + * @brief 将JSON格式的值转换为Neuron内部的标签值格式 + * + * 根据JSON值的类型,将其转换为对应的neu_dvalue_t类型,支持整数、字符串、 + * 浮点数、布尔值和字节数组等多种数据类型 + * + * @param req JSON值指针 + * @param t JSON值的类型 + * @param value 目标neu_dvalue_t结构体指针 + * @return 成功返回0,失败返回-1 + */ static int json_value_to_tag_value(union neu_json_value *req, enum neu_json_type t, neu_dvalue_t *value) { switch (t) { - case NEU_JSON_INT: + case NEU_JSON_INT: // 整数类型 value->type = NEU_TYPE_INT64; value->value.u64 = req->val_int; break; - case NEU_JSON_STR: + case NEU_JSON_STR: // 字符串类型 value->type = NEU_TYPE_STRING; strncpy(value->value.str, req->val_str, sizeof(value->value.str)); break; - case NEU_JSON_DOUBLE: + case NEU_JSON_DOUBLE: // 双精度浮点类型 value->type = NEU_TYPE_DOUBLE; value->value.d64 = req->val_double; break; - case NEU_JSON_BOOL: + case NEU_JSON_BOOL: // 布尔类型 value->type = NEU_TYPE_BOOL; value->value.boolean = req->val_bool; break; - case NEU_JSON_BYTES: + case NEU_JSON_BYTES: // 字节数组类型 value->type = NEU_TYPE_BYTES; value->value.bytes.length = req->val_bytes.length; memcpy(value->value.bytes.bytes, req->val_bytes.bytes, @@ -184,6 +257,16 @@ static int json_value_to_tag_value(union neu_json_value *req, return 0; } +/** + * @brief 发送写入单个标签值的请求 + * + * 通过MQTT插件将收到的写入请求转发给相应的驱动程序 + * + * @param plugin 插件实例 + * @param mqtt MQTT上下文信息 + * @param req 写入请求参数 + * @return 成功返回0,失败返回-1 + */ static int send_write_tag_req(neu_plugin_t *plugin, neu_json_mqtt_t *mqtt, neu_json_write_req_t *req) { @@ -193,18 +276,21 @@ static int send_write_tag_req(neu_plugin_t *plugin, neu_json_mqtt_t *mqtt, neu_reqresp_head_t header = { 0 }; neu_req_write_tag_t cmd = { 0 }; - header.ctx = mqtt; - header.type = NEU_REQ_WRITE_TAG; + header.ctx = mqtt; // 设置上下文为MQTT会话信息 + header.type = NEU_REQ_WRITE_TAG; // 请求类型为写入单个标签 + // 配置写入参数 cmd.driver = req->node; cmd.group = req->group; cmd.tag = req->tag; + // 将JSON值转换为Neuron内部标签值格式 if (0 != json_value_to_tag_value(&req->value, req->t, &cmd.value)) { plog_error(plugin, "invalid tag value type: %d", req->t); return -1; } + // 执行写入操作 if (0 != neu_plugin_op(plugin, header, &cmd)) { plog_error(plugin, "neu_plugin_op(NEU_REQ_WRITE_TAG) fail"); return -1; @@ -216,12 +302,23 @@ static int send_write_tag_req(neu_plugin_t *plugin, neu_json_mqtt_t *mqtt, return 0; } +/** + * @brief 发送批量写入多个标签值的请求 + * + * 通过MQTT插件将收到的批量写入请求转发给相应的驱动程序,处理多个标签值的同时写入 + * + * @param plugin 插件实例 + * @param mqtt MQTT上下文信息 + * @param req 批量写入请求参数 + * @return 成功返回0,失败返回-1 + */ static int send_write_tags_req(neu_plugin_t *plugin, neu_json_mqtt_t *mqtt, neu_json_write_tags_req_t *req) { plog_notice(plugin, "write tags uuid:%s, group:%s, node:%s", mqtt->uuid, req->group, req->node); + // 验证所有字符串类型的值长度是否合法 for (int i = 0; i < req->n_tag; i++) { if (req->tags[i].t == NEU_JSON_STR) { if (strlen(req->tags[i].value.val_str) >= NEU_VALUE_SIZE) { @@ -230,11 +327,13 @@ static int send_write_tags_req(neu_plugin_t *plugin, neu_json_mqtt_t *mqtt, } } + // 初始化请求头 neu_reqresp_head_t header = { .ctx = mqtt, .type = NEU_REQ_WRITE_TAGS, }; + // 配置批量写入命令 neu_req_write_tags_t cmd = { 0 }; cmd.driver = req->node; cmd.group = req->group; @@ -244,6 +343,7 @@ static int send_write_tags_req(neu_plugin_t *plugin, neu_json_mqtt_t *mqtt, return -1; } + // 转换每个标签值到内部格式 for (int i = 0; i < cmd.n_tag; i++) { strcpy(cmd.tags[i].tag, req->tags[i].tag); if (0 != @@ -255,54 +355,90 @@ static int send_write_tags_req(neu_plugin_t *plugin, neu_json_mqtt_t *mqtt, } } + // 执行批量写入操作 if (0 != neu_plugin_op(plugin, header, &cmd)) { plog_error(plugin, "neu_plugin_op(NEU_REQ_WRITE_TAGS) fail"); free(cmd.tags); return -1; } - req->node = NULL; // ownership moved - req->group = NULL; // ownership moved + // 传递所有权,避免double free + req->node = NULL; // 所有权转移 + req->group = NULL; // 所有权转移 return 0; } +/** + * @brief MQTT消息发布回调函数 + * + * 当消息发布完成后(无论成功或失败)调用此回调函数,用于更新相关指标并清理资源 + * + * @param errcode 错误码,0表示成功 + * @param qos MQTT服务质量级别 + * @param topic 发布的主题 + * @param payload 消息负载 + * @param len 消息长度 + * @param data 用户数据(插件指针) + */ static void publish_cb(int errcode, neu_mqtt_qos_e qos, char *topic, uint8_t *payload, uint32_t len, void *data) { - (void) qos; - (void) topic; - (void) len; + (void) qos; // 未使用的参数 + (void) topic; // 未使用的参数 + (void) len; // 未使用的参数 neu_plugin_t *plugin = data; + // 获取指标更新回调函数 neu_adapter_update_metric_cb_t update_metric = plugin->common.adapter_callbacks->update_metric; + // 根据发布结果更新相应指标 if (0 == errcode) { + // 发布成功,增加成功计数 update_metric(plugin->common.adapter, NEU_METRIC_SEND_MSGS_TOTAL, 1, NULL); } else { + // 发布失败,增加错误计数 update_metric(plugin->common.adapter, NEU_METRIC_SEND_MSG_ERRORS_TOTAL, 1, NULL); } + // 释放消息负载内存 free(payload); } +/** + * @brief 发布MQTT消息 + * + * 将消息发布到指定的MQTT主题 + * + * @param plugin 插件实例 + * @param qos MQTT服务质量级别 + * @param topic 发布的主题 + * @param payload 消息内容 + * @param payload_len 消息长度 + * @return 成功返回0,失败返回-1 + */ static inline int publish(neu_plugin_t *plugin, neu_mqtt_qos_e qos, char *topic, char *payload, size_t payload_len) { + // 获取指标更新回调函数 neu_adapter_update_metric_cb_t update_metric = plugin->common.adapter_callbacks->update_metric; + // 调用MQTT客户端API发布消息 int rv = neu_mqtt_client_publish(plugin->client, qos, topic, (uint8_t *) payload, (uint32_t) payload_len, plugin, publish_cb); if (0 != rv) { + // 发布失败处理 plog_error(plugin, "pub [%s, QoS%d] fail", topic, qos); + // 增加错误计数 update_metric(plugin->common.adapter, NEU_METRIC_SEND_MSG_ERRORS_TOTAL, 1, NULL); + // 清理资源 free(payload); rv = NEU_ERR_MQTT_PUBLISH_FAILURE; } @@ -310,20 +446,33 @@ static inline int publish(neu_plugin_t *plugin, neu_mqtt_qos_e qos, char *topic, return rv; } +/** + * @brief 处理MQTT写请求消息 + * + * 解析MQTT接收到的写入请求,根据类型(单个或批量)转发给相应的处理函数 + * + * @param qos MQTT服务质量级别 + * @param topic 接收消息的主题 + * @param payload 消息负载 + * @param len 消息长度 + * @param data 用户数据(插件指针) + */ void handle_write_req(neu_mqtt_qos_e qos, const char *topic, const uint8_t *payload, uint32_t len, void *data) { int rv = 0; - neu_plugin_t * plugin = data; + neu_plugin_t *plugin = data; neu_json_write_t *req = NULL; - (void) qos; - (void) topic; + (void) qos; // 未使用的参数 + (void) topic; // 未使用的参数 + // 更新接收消息计数器 neu_adapter_update_metric_cb_t update_metric = plugin->common.adapter_callbacks->update_metric; update_metric(plugin->common.adapter, NEU_METRIC_RECV_MSGS_TOTAL, 1, NULL); + // 为JSON字符串分配内存并复制数据 char *json_str = malloc(len + 1); if (NULL == json_str) { return; @@ -332,6 +481,7 @@ void handle_write_req(neu_mqtt_qos_e qos, const char *topic, memcpy(json_str, payload, len); json_str[len] = '\0'; + // 解析MQTT请求头 neu_json_mqtt_t *mqtt = NULL; rv = neu_json_decode_mqtt_req(json_str, &mqtt); if (0 != rv) { @@ -340,6 +490,7 @@ void handle_write_req(neu_mqtt_qos_e qos, const char *topic, return; } + // 解析写入请求内容 rv = neu_json_decode_write(json_str, &req); if (0 != rv) { plog_error(plugin, "neu_json_decode_write fail"); @@ -348,37 +499,54 @@ void handle_write_req(neu_mqtt_qos_e qos, const char *topic, return; } + // 根据请求类型选择处理函数 if (req->singular) { + // 单个标签写入 rv = send_write_tag_req(plugin, mqtt, &req->single); } else { + // 批量标签写入 rv = send_write_tags_req(plugin, mqtt, &req->plural); } if (0 != rv) { neu_json_decode_mqtt_req_free(mqtt); } + // 清理资源 neu_json_decode_write_free(req); free(json_str); } +/** + * @brief 处理写操作的响应并发送结果 + * + * 生成写操作响应的JSON数据并发布到MQTT主题 + * + * @param plugin 插件实例 + * @param mqtt_json MQTT请求上下文 + * @param data 响应错误数据 + * @return 成功返回0,失败返回错误代码 + */ int handle_write_response(neu_plugin_t *plugin, neu_json_mqtt_t *mqtt_json, neu_resp_error_t *data) { int rv = 0; char *json_str = NULL; + // 检查MQTT客户端是否可用 if (NULL == plugin->client) { rv = NEU_ERR_MQTT_IS_NULL; goto end; } + // 检查MQTT连接状态和缓存配置 if (0 == plugin->config.cache && !neu_mqtt_client_is_connected(plugin->client)) { - // cache disable and we are disconnected + // 缓存已禁用且MQTT已断开连接,无法处理 rv = NEU_ERR_MQTT_FAILURE; goto end; } + // 生成写操作响应的JSON数据 json_str = generate_write_resp_json(plugin, mqtt_json, data); if (NULL == json_str) { plog_error(plugin, "generate write resp json fail, uuid:%s", @@ -387,30 +555,45 @@ int handle_write_response(neu_plugin_t *plugin, neu_json_mqtt_t *mqtt_json, goto end; } - char * topic = plugin->config.write_resp_topic; + // 发布响应到指定主题 + char *topic = plugin->config.write_resp_topic; neu_mqtt_qos_e qos = plugin->config.qos; rv = publish(plugin, qos, topic, json_str, strlen(json_str)); - json_str = NULL; + json_str = NULL; // 所有权已转移给publish函数 end: + // 清理资源 neu_json_decode_mqtt_req_free(mqtt_json); return rv; } +/** + * @brief 处理MQTT读取请求消息 + * + * 解析MQTT接收到的读取请求,根据请求内容向驱动发送读取命令 + * + * @param qos MQTT服务质量级别 + * @param topic 接收消息的主题 + * @param payload 消息负载 + * @param len 消息长度 + * @param data 用户数据(插件指针) + */ void handle_read_req(neu_mqtt_qos_e qos, const char *topic, const uint8_t *payload, uint32_t len, void *data) { int rv = 0; - neu_plugin_t * plugin = data; + neu_plugin_t *plugin = data; neu_json_read_req_t *req = NULL; - (void) qos; - (void) topic; + (void) qos; // 未使用的参数 + (void) topic; // 未使用的参数 + // 更新接收消息计数器 neu_adapter_update_metric_cb_t update_metric = plugin->common.adapter_callbacks->update_metric; update_metric(plugin->common.adapter, NEU_METRIC_RECV_MSGS_TOTAL, 1, NULL); + // 为JSON字符串分配内存并复制数据 char *json_str = malloc(len + 1); if (NULL == json_str) { return; @@ -469,7 +652,7 @@ int handle_read_response(neu_plugin_t *plugin, neu_json_mqtt_t *mqtt_json, goto end; } - char * topic = plugin->read_resp_topic; + char *topic = plugin->read_resp_topic; neu_mqtt_qos_e qos = plugin->config.qos; rv = publish(plugin, qos, topic, json_str, strlen(json_str)); json_str = NULL; @@ -479,7 +662,7 @@ int handle_read_response(neu_plugin_t *plugin, neu_json_mqtt_t *mqtt_json, return rv; } -int handle_trans_data(neu_plugin_t * plugin, +int handle_trans_data(neu_plugin_t *plugin, neu_reqresp_trans_data_t *trans_data) { int rv = 0; @@ -509,7 +692,7 @@ int handle_trans_data(neu_plugin_t * plugin, return NEU_ERR_EINTERNAL; } - char * topic = route->topic; + char *topic = route->topic; neu_mqtt_qos_e qos = plugin->config.qos; rv = publish(plugin, qos, topic, json_str, strlen(json_str)); json_str = NULL; @@ -592,7 +775,7 @@ int handle_update_subscribe(neu_plugin_t *plugin, neu_req_subscribe_t *sub_info) return rv; } -int handle_unsubscribe_group(neu_plugin_t * plugin, +int handle_unsubscribe_group(neu_plugin_t *plugin, neu_req_unsubscribe_t *unsub_info) { route_tbl_del(&plugin->route_tbl, unsub_info->driver, unsub_info->group); diff --git a/plugins/mqtt/mqtt_plugin.c b/plugins/mqtt/mqtt_plugin.c index 16cd2ef05..b47bce9cb 100644 --- a/plugins/mqtt/mqtt_plugin.c +++ b/plugins/mqtt/mqtt_plugin.c @@ -26,28 +26,59 @@ const neu_plugin_module_t neu_plugin_module; +/** + * @brief MQTT连接成功回调函数 + * + * 当MQTT客户端成功连接到服务器时调用此函数 + * + * @param data 插件实例指针 + */ static void connect_cb(void *data) { - neu_plugin_t *plugin = data; - plugin->common.link_state = NEU_NODE_LINK_STATE_CONNECTED; + neu_plugin_t *plugin = data; + plugin->common.link_state = + NEU_NODE_LINK_STATE_CONNECTED; // 更新连接状态为已连接 plog_notice(plugin, "plugin `%s` connected", neu_plugin_module.module_name); } +/** + * @brief MQTT断开连接回调函数 + * + * 当MQTT客户端与服务器断开连接时调用此函数 + * + * @param data 插件实例指针 + */ static void disconnect_cb(void *data) { - neu_plugin_t *plugin = data; - plugin->common.link_state = NEU_NODE_LINK_STATE_DISCONNECTED; + neu_plugin_t *plugin = data; + plugin->common.link_state = + NEU_NODE_LINK_STATE_DISCONNECTED; // 更新连接状态为已断开 plog_notice(plugin, "plugin `%s` disconnected", neu_plugin_module.module_name); } +/** + * @brief 创建并初始化MQTT插件实例 + * + * 分配和初始化MQTT插件实例所需的内存 + * + * @return 成功返回插件实例指针,失败返回NULL + */ static neu_plugin_t *mqtt_plugin_open(void) { neu_plugin_t *plugin = (neu_plugin_t *) calloc(1, sizeof(neu_plugin_t)); - neu_plugin_common_init(&plugin->common); + neu_plugin_common_init(&plugin->common); // 初始化插件通用部分 return plugin; } +/** + * @brief 释放MQTT插件实例 + * + * 释放插件实例占用的资源 + * + * @param plugin 插件实例指针 + * @return 总是返回NEU_ERR_SUCCESS表示成功 + */ static int mqtt_plugin_close(neu_plugin_t *plugin) { const char *name = neu_plugin_module.module_name; @@ -57,11 +88,22 @@ static int mqtt_plugin_close(neu_plugin_t *plugin) return NEU_ERR_SUCCESS; } +/** + * @brief 初始化MQTT插件 + * + * 执行插件初始化操作,注册指标监控等 + * + * @param plugin 插件实例指针 + * @param load 是否是加载插件时初始化 + * @return 成功返回NEU_ERR_SUCCESS,失败返回错误代码 + */ static int mqtt_plugin_init(neu_plugin_t *plugin, bool load) { - (void) load; + (void) load; // 未使用的参数 plog_notice(plugin, "initialize plugin `%s` success", neu_plugin_module.module_name); + + // 注册缓存消息数量指标,用于监控 neu_adapter_register_metric_cb_t register_metric = plugin->common.adapter_callbacks->register_metric; register_metric(plugin->common.adapter, NEU_METRIC_CACHED_MSGS_NUM, @@ -70,20 +112,33 @@ static int mqtt_plugin_init(neu_plugin_t *plugin, bool load) return NEU_ERR_SUCCESS; } +/** + * @brief 反初始化MQTT插件 + * + * 释放插件使用的资源,关闭MQTT连接,清理配置 + * + * @param plugin 插件实例指针 + * @return 成功返回NEU_ERR_SUCCESS,失败返回错误代码 + */ static int mqtt_plugin_uninit(neu_plugin_t *plugin) { + // 释放配置资源 mqtt_config_fini(&plugin->config); + + // 关闭并释放MQTT客户端 if (plugin->client) { neu_mqtt_client_close(plugin->client); neu_mqtt_client_free(plugin->client); plugin->client = NULL; } + // 释放主题字符串 free(plugin->read_req_topic); plugin->read_req_topic = NULL; free(plugin->read_resp_topic); plugin->read_resp_topic = NULL; + // 释放路由表 route_tbl_free(plugin->route_tbl); plog_notice(plugin, "uninitialize plugin `%s` success", @@ -91,6 +146,16 @@ static int mqtt_plugin_uninit(neu_plugin_t *plugin) return NEU_ERR_SUCCESS; } +/** + * @brief 配置MQTT客户端参数 + * + * 根据配置信息设置MQTT客户端的连接参数、回调函数等 + * + * @param plugin 插件实例指针 + * @param client MQTT客户端实例 + * @param config MQTT配置信息 + * @return 成功返回0,失败返回-1 + */ static int config_mqtt_client(neu_plugin_t *plugin, neu_mqtt_client_t *client, const mqtt_config_t *config) { @@ -100,37 +165,42 @@ static int config_mqtt_client(neu_plugin_t *plugin, neu_mqtt_client_t *client, return 0; } - // set log category as soon as possible to ease debugging + // 尽早设置日志类别,便于调试 rv = neu_mqtt_client_set_zlog_category(client, plugin->common.log); if (0 != rv) { plog_error(plugin, "neu_mqtt_client_set_zlog_category fail"); return -1; } + // 设置MQTT服务器地址和端口 rv = neu_mqtt_client_set_addr(client, config->host, config->port); if (0 != rv) { plog_error(plugin, "neu_mqtt_client_set_host fail"); return -1; } + // 设置MQTT客户端ID rv = neu_mqtt_client_set_id(client, config->client_id); if (0 != rv) { plog_error(plugin, "neu_mqtt_client_set_id fail"); return -1; } + // 设置连接成功回调函数 rv = neu_mqtt_client_set_connect_cb(client, connect_cb, plugin); if (0 != rv) { plog_error(plugin, "neu_mqtt_client_set_connect_cb fail"); return -1; } + // 设置断开连接回调函数 rv = neu_mqtt_client_set_disconnect_cb(client, disconnect_cb, plugin); if (0 != rv) { plog_error(plugin, "neu_mqtt_client_set_disconnect_cb fail"); return -1; } + // 设置消息缓存大小(内存和磁盘) rv = neu_mqtt_client_set_cache_size(client, config->cache_mem_size, config->cache_disk_size); if (0 != rv) { @@ -138,6 +208,7 @@ static int config_mqtt_client(neu_plugin_t *plugin, neu_mqtt_client_t *client, return -1; } + // 设置缓存同步间隔 rv = neu_mqtt_client_set_cache_sync_interval(client, config->cache_sync_interval); if (0 != rv) { @@ -145,6 +216,7 @@ static int config_mqtt_client(neu_plugin_t *plugin, neu_mqtt_client_t *client, return -1; } + // 设置用户名和密码(如果提供) if (NULL != config->username) { rv = neu_mqtt_client_set_user(client, config->username, config->password); @@ -153,6 +225,7 @@ static int config_mqtt_client(neu_plugin_t *plugin, neu_mqtt_client_t *client, } } + // 设置TLS/SSL参数(如果启用) rv = neu_mqtt_client_set_tls(client, config->ssl, config->ca, config->cert, config->key, config->keypass); if (0 != rv) { @@ -163,19 +236,29 @@ static int config_mqtt_client(neu_plugin_t *plugin, neu_mqtt_client_t *client, return rv; } +/** + * @brief 创建MQTT主题 + * + * 根据插件名称生成标准格式的读请求和读响应主题 + * + * @param plugin 插件实例指针 + * @return 成功返回0,失败返回-1 + */ static int create_topic(neu_plugin_t *plugin) { if (plugin->read_req_topic) { - // topics already created + // 主题已创建 return 0; } + // 创建读请求主题:/neuron/插件名/read/req neu_asprintf(&plugin->read_req_topic, "/neuron/%s/read/req", plugin->common.name); if (NULL == plugin->read_req_topic) { return -1; } + // 创建读响应主题:/neuron/插件名/read/resp neu_asprintf(&plugin->read_resp_topic, "/neuron/%s/read/resp", plugin->common.name); if (NULL == plugin->read_resp_topic) { @@ -187,13 +270,24 @@ static int create_topic(neu_plugin_t *plugin) return 0; } +/** + * @brief 订阅MQTT主题 + * + * 订阅读取和写入请求主题,设置相应的处理回调函数 + * + * @param plugin 插件实例指针 + * @param config MQTT配置信息 + * @return 成功返回0,失败返回错误代码 + */ static int subscribe(neu_plugin_t *plugin, const mqtt_config_t *config) { + // 创建主题(如果还未创建) if (0 != create_topic(plugin)) { plog_error(plugin, "create topics fail"); return NEU_ERR_EINTERNAL; } + // 订阅读取请求主题 if (0 != neu_mqtt_client_subscribe(plugin->client, config->qos, plugin->read_req_topic, plugin, @@ -202,6 +296,7 @@ static int subscribe(neu_plugin_t *plugin, const mqtt_config_t *config) return NEU_ERR_MQTT_SUBSCRIBE_FAILURE; } + // 订阅写入请求主题 if (0 != neu_mqtt_client_subscribe(plugin->client, config->qos, config->write_req_topic, plugin, @@ -214,21 +309,41 @@ static int subscribe(neu_plugin_t *plugin, const mqtt_config_t *config) return 0; } +/** + * @brief 取消订阅MQTT主题 + * + * 取消订阅之前订阅的主题 + * + * @param plugin 插件实例指针 + * @param config MQTT配置信息 + * @return 总是返回0表示成功 + */ static int unsubscribe(neu_plugin_t *plugin, const mqtt_config_t *config) { + // 取消订阅读请求和写请求主题 neu_mqtt_client_unsubscribe(plugin->client, plugin->read_req_topic); neu_mqtt_client_unsubscribe(plugin->client, config->write_req_topic); - neu_msleep(100); // wait for message completion + neu_msleep(100); // 等待消息处理完成 return 0; } +/** + * @brief 配置MQTT插件 + * + * 解析设置字符串并应用MQTT配置 + * + * @param plugin 插件实例指针 + * @param setting JSON格式的配置字符串 + * @return 成功返回0,失败返回错误代码 + */ static int mqtt_plugin_config(neu_plugin_t *plugin, const char *setting) { int rv = 0; - const char * plugin_name = neu_plugin_module.module_name; + const char *plugin_name = neu_plugin_module.module_name; mqtt_config_t config = { 0 }; bool started = false; + // 解析配置字符串 rv = mqtt_config_parse(plugin, setting, &config); if (0 != rv) { plog_error(plugin, "neu_mqtt_config_parse fail"); @@ -236,6 +351,7 @@ static int mqtt_plugin_config(neu_plugin_t *plugin, const char *setting) } if (NULL == plugin->client) { + // 首次配置,创建MQTT客户端 plugin->client = neu_mqtt_client_new(NEU_MQTT_VERSION_V311); if (NULL == plugin->client) { plog_error(plugin, "neu_mqtt_client_new fail"); @@ -243,6 +359,7 @@ static int mqtt_plugin_config(neu_plugin_t *plugin, const char *setting) goto error; } } else if (neu_mqtt_client_is_open(plugin->client)) { + // 客户端已存在且已连接,需要先关闭 started = true; unsubscribe(plugin, &plugin->config); rv = neu_mqtt_client_close(plugin->client); @@ -253,6 +370,7 @@ static int mqtt_plugin_config(neu_plugin_t *plugin, const char *setting) } } + // 配置MQTT客户端参数 rv = config_mqtt_client(plugin, plugin->client, &config); if (0 != rv) { rv = NEU_ERR_MQTT_INIT_FAILURE; @@ -260,6 +378,7 @@ static int mqtt_plugin_config(neu_plugin_t *plugin, const char *setting) } if (started) { + // 如果之前已启动,则重新连接并订阅主题 if (0 != neu_mqtt_client_open(plugin->client)) { plog_error(plugin, "neu_mqtt_client_open fail"); rv = NEU_ERR_MQTT_CONNECT_FAILURE; @@ -271,9 +390,10 @@ static int mqtt_plugin_config(neu_plugin_t *plugin, const char *setting) } if (plugin->config.host) { - // already configured + // 已存在旧配置,释放旧配置 mqtt_config_fini(&plugin->config); } + // 保存新配置 memmove(&plugin->config, &config, sizeof(config)); plog_notice(plugin, "config plugin `%s` success", plugin_name); @@ -285,23 +405,34 @@ static int mqtt_plugin_config(neu_plugin_t *plugin, const char *setting) return rv; } +/** + * @brief 启动MQTT插件 + * + * 连接MQTT服务器并订阅主题 + * + * @param plugin 插件实例指针 + * @return 成功返回0,失败返回错误代码 + */ static int mqtt_plugin_start(neu_plugin_t *plugin) { int rv = 0; const char *plugin_name = neu_plugin_module.module_name; + // 检查客户端是否已初始化 if (NULL == plugin->client) { plog_error(plugin, "mqtt client is NULL due to init failure"); rv = NEU_ERR_MQTT_IS_NULL; goto end; } + // 连接MQTT服务器 if (0 != neu_mqtt_client_open(plugin->client)) { plog_error(plugin, "neu_mqtt_client_open fail"); rv = NEU_ERR_MQTT_CONNECT_FAILURE; goto end; } + // 订阅相关主题 rv = subscribe(plugin, &plugin->config); end: @@ -310,14 +441,24 @@ static int mqtt_plugin_start(neu_plugin_t *plugin) } else { plog_error(plugin, "start plugin `%s` failed, error %d", plugin_name, rv); + // 失败时关闭连接 neu_mqtt_client_close(plugin->client); } return rv; } +/** + * @brief 停止MQTT插件 + * + * 取消订阅并断开MQTT连接 + * + * @param plugin 插件实例指针 + * @return 总是返回NEU_ERR_SUCCESS表示成功 + */ static int mqtt_plugin_stop(neu_plugin_t *plugin) { if (plugin->client) { + // 取消订阅并关闭连接 unsubscribe(plugin, &plugin->config); neu_mqtt_client_close(plugin->client); plog_notice(plugin, "mqtt client closed"); @@ -328,15 +469,26 @@ static int mqtt_plugin_stop(neu_plugin_t *plugin) return NEU_ERR_SUCCESS; } +/** + * @brief 处理插件收到的请求 + * + * 根据请求类型分发到不同的处理函数 + * + * @param plugin 插件实例指针 + * @param head 请求头信息 + * @param data 请求数据 + * @return 处理结果错误码 + */ static int mqtt_plugin_request(neu_plugin_t *plugin, neu_reqresp_head_t *head, void *data) { neu_err_code_e error = NEU_ERR_SUCCESS; + // 获取指标更新回调函数 neu_adapter_update_metric_cb_t update_metric = plugin->common.adapter_callbacks->update_metric; - // update cached messages number per seconds + // 每秒更新一次缓存消息数量指标 if (NULL != plugin->client && (global_timestamp - plugin->cache_metric_update_ts) >= 1000) { update_metric(plugin->common.adapter, NEU_METRIC_CACHED_MSGS_NUM, @@ -345,39 +497,40 @@ static int mqtt_plugin_request(neu_plugin_t *plugin, neu_reqresp_head_t *head, plugin->cache_metric_update_ts = global_timestamp; } + // 根据请求类型分发到不同的处理函数 switch (head->type) { - case NEU_RESP_ERROR: + case NEU_RESP_ERROR: // 写操作响应 error = handle_write_response(plugin, head->ctx, data); break; - case NEU_RESP_READ_GROUP: + case NEU_RESP_READ_GROUP: // 读取组响应 error = handle_read_response(plugin, head->ctx, data); break; - case NEU_REQRESP_TRANS_DATA: { + case NEU_REQRESP_TRANS_DATA: { // 数据传输 error = handle_trans_data(plugin, data); break; } - case NEU_REQ_SUBSCRIBE_GROUP: + case NEU_REQ_SUBSCRIBE_GROUP: // 订阅组 error = handle_subscribe_group(plugin, data); break; - case NEU_REQ_UPDATE_SUBSCRIBE_GROUP: + case NEU_REQ_UPDATE_SUBSCRIBE_GROUP: // 更新订阅 error = handle_update_subscribe(plugin, data); break; - case NEU_REQ_UNSUBSCRIBE_GROUP: + case NEU_REQ_UNSUBSCRIBE_GROUP: // 取消订阅组 error = handle_unsubscribe_group(plugin, data); break; - case NEU_REQ_UPDATE_GROUP: + case NEU_REQ_UPDATE_GROUP: // 更新组 error = handle_update_group(plugin, data); break; - case NEU_REQ_DEL_GROUP: + case NEU_REQ_DEL_GROUP: // 删除组 error = handle_del_group(plugin, data); break; - case NEU_REQ_UPDATE_NODE: + case NEU_REQ_UPDATE_NODE: // 更新节点 error = handle_update_driver(plugin, data); break; - case NEU_REQRESP_NODE_DELETED: + case NEU_REQRESP_NODE_DELETED: // 节点已删除 error = handle_del_driver(plugin, data); break; - default: + default: // 未知请求类型 error = NEU_ERR_MQTT_FAILURE; break; } @@ -385,29 +538,39 @@ static int mqtt_plugin_request(neu_plugin_t *plugin, neu_reqresp_head_t *head, return error; } +/** + * @brief MQTT插件接口函数集合 + * + * 定义插件的各种操作接口,包括打开、关闭、初始化、启动等 + */ static const neu_plugin_intf_funs_t plugin_intf_funs = { - .open = mqtt_plugin_open, - .close = mqtt_plugin_close, - .init = mqtt_plugin_init, - .uninit = mqtt_plugin_uninit, - .start = mqtt_plugin_start, - .stop = mqtt_plugin_stop, - .setting = mqtt_plugin_config, - .request = mqtt_plugin_request, + .open = mqtt_plugin_open, // 创建插件实例 + .close = mqtt_plugin_close, // 释放插件实例 + .init = mqtt_plugin_init, // 初始化插件 + .uninit = mqtt_plugin_uninit, // 反初始化插件 + .start = mqtt_plugin_start, // 启动插件 + .stop = mqtt_plugin_stop, // 停止插件 + .setting = mqtt_plugin_config, // 配置插件 + .request = mqtt_plugin_request, // 处理请求 }; #define DESCRIPTION "Northbound MQTT plugin bases on NanoSDK." #define DESCRIPTION_ZH "基于 NanoSDK 的北向应用 MQTT 插件" +/** + * @brief MQTT插件模块定义 + * + * 定义插件的基本信息,包括名称、版本、描述、类型等 + */ const neu_plugin_module_t neu_plugin_module = { - .version = NEURON_PLUGIN_VER_1_0, - .schema = "mqtt", - .module_name = "MQTT", - .module_descr = DESCRIPTION, - .module_descr_zh = DESCRIPTION_ZH, - .intf_funs = &plugin_intf_funs, - .kind = NEU_PLUGIN_KIND_SYSTEM, - .type = NEU_NA_TYPE_APP, - .display = true, - .single = false, + .version = NEURON_PLUGIN_VER_1_0, // 插件版本 + .schema = "mqtt", // 模式标识 + .module_name = "MQTT", // 模块名称 + .module_descr = DESCRIPTION, // 英文描述 + .module_descr_zh = DESCRIPTION_ZH, // 中文描述 + .intf_funs = &plugin_intf_funs, // 接口函数集 + .kind = NEU_PLUGIN_KIND_SYSTEM, // 插件类型(系统插件) + .type = NEU_NA_TYPE_APP, // 插件应用类型(北向应用) + .display = true, // 是否显示在UI中 + .single = false, // 是否只能创建单个实例 }; diff --git a/src/base/group.c b/src/base/group.c index 99112755e..cf70e06c5 100644 --- a/src/base/group.c +++ b/src/base/group.c @@ -25,77 +25,113 @@ #include "group.h" +/** + * @brief 标签元素结构,用于在哈希表中存储标签信息 + */ typedef struct tag_elem { - char *name; - - neu_datatag_t *tag; - - UT_hash_handle hh; + char *name; /**< 标签名称,作为哈希表的键 */ + neu_datatag_t *tag; /**< 标签详细信息 */ + UT_hash_handle hh; /**< uthash哈希句柄 */ } tag_elem_t; +/** + * @brief 组结构定义,用于管理一组相关的数据标签 + */ struct neu_group { - char *name; - - tag_elem_t *tags; - uint32_t interval; - - int64_t timestamp; - pthread_mutex_t mtx; + char *name; /**< 组名称 */ + tag_elem_t *tags; /**< 包含的标签哈希表 */ + uint32_t interval; /**< 组轮询间隔(毫秒) */ + int64_t timestamp; /**< 组最后修改的时间戳 */ + pthread_mutex_t mtx; /**< 互斥锁,保护组内数据的并发访问 */ }; -static UT_array *to_array(tag_elem_t *tags); -static UT_array *to_read_array(tag_elem_t *tags); -static void split_static_array(tag_elem_t *tags, UT_array **static_tags, - UT_array **other_tags); -static void update_timestamp(neu_group_t *group); +/* 函数声明 */ +static UT_array *to_array(tag_elem_t *tags); /**< 将标签哈希表转换为数组 */ +static UT_array *to_read_array(tag_elem_t *tags); /**< 提取可读标签到数组 */ +static void + split_static_array(tag_elem_t *tags, UT_array **static_tags, + UT_array **other_tags); /**< 分离静态标签和其他标签 */ +static void update_timestamp(neu_group_t *group); /**< 更新组的时间戳 */ +/** + * @brief 创建新的组实例 + * + * @param name 组名称 + * @param interval 组轮询间隔(毫秒) + * @return 成功返回组实例指针,失败返回NULL + */ neu_group_t *neu_group_new(const char *name, uint32_t interval) { neu_group_t *group = calloc(1, sizeof(neu_group_t)); - group->name = strdup(name); - group->interval = interval; - pthread_mutex_init(&group->mtx, NULL); + group->name = strdup(name); /* 复制组名称 */ + group->interval = interval; /* 设置轮询间隔 */ + pthread_mutex_init(&group->mtx, NULL); /* 初始化互斥锁 */ return group; } +/** + * @brief 销毁组实例并释放所有资源 + * + * @param group 要销毁的组实例 + */ void neu_group_destroy(neu_group_t *group) { tag_elem_t *el = NULL, *tmp = NULL; pthread_mutex_lock(&group->mtx); + /* 遍历并释放所有标签元素 */ HASH_ITER(hh, group->tags, el, tmp) { - HASH_DEL(group->tags, el); - free(el->name); - neu_tag_free(el->tag); - free(el); + HASH_DEL(group->tags, el); /* 从哈希表中移除 */ + free(el->name); /* 释放标签名称 */ + neu_tag_free(el->tag); /* 释放标签资源 */ + free(el); /* 释放标签元素结构 */ } pthread_mutex_unlock(&group->mtx); - pthread_mutex_destroy(&group->mtx); - free(group->name); - free(group); + pthread_mutex_destroy(&group->mtx); /* 销毁互斥锁 */ + free(group->name); /* 释放组名称 */ + free(group); /* 释放组结构 */ } +/** + * @brief 获取组的名称 + * + * @param group 组实例 + * @return 组名称 + */ const char *neu_group_get_name(const neu_group_t *group) { return group->name; } +/** + * @brief 设置组的名称 + * + * @param group 组实例 + * @param name 新的组名称 + * @return 成功返回0,失败返回错误码 + */ int neu_group_set_name(neu_group_t *group, const char *name) { char *new_name = NULL; if (NULL == name || NULL == (new_name = strdup(name))) { - return NEU_ERR_EINTERNAL; + return NEU_ERR_EINTERNAL; /* 内存分配失败 */ } - free(group->name); - group->name = new_name; + free(group->name); /* 释放旧名称 */ + group->name = new_name; /* 设置新名称 */ return 0; } +/** + * @brief 获取组的轮询间隔 + * + * @param group 组实例 + * @return 轮询间隔(毫秒) + */ uint32_t neu_group_get_interval(const neu_group_t *group) { uint32_t interval = 0; @@ -105,54 +141,85 @@ uint32_t neu_group_get_interval(const neu_group_t *group) return interval; } +/** + * @brief 设置组的轮询间隔 + * + * @param group 组实例 + * @param interval 新的轮询间隔(毫秒) + */ void neu_group_set_interval(neu_group_t *group, uint32_t interval) { group->interval = interval; } +/** + * @brief 更新组的轮询间隔,并在发生变化时更新时间戳 + * + * @param group 组实例 + * @param interval 新的轮询间隔(毫秒) + * @return 成功返回0 + */ int neu_group_update(neu_group_t *group, uint32_t interval) { - if (group->interval != interval) { - group->interval = interval; - update_timestamp(group); + if (group->interval != interval) { /* 间隔发生变化 */ + group->interval = interval; /* 更新间隔 */ + update_timestamp(group); /* 更新时间戳 */ } return 0; } +/** + * @brief 向组中添加标签 + * + * @param group 组实例 + * @param tag 要添加的标签 + * @return 成功返回0,失败返回错误码 + */ int neu_group_add_tag(neu_group_t *group, const neu_datatag_t *tag) { tag_elem_t *el = NULL; pthread_mutex_lock(&group->mtx); + /* 检查是否已存在同名标签 */ HASH_FIND_STR(group->tags, tag->name, el); if (el != NULL) { pthread_mutex_unlock(&group->mtx); - return NEU_ERR_TAG_NAME_CONFLICT; + return NEU_ERR_TAG_NAME_CONFLICT; /* 标签名称冲突 */ } + /* 创建新的标签元素 */ el = calloc(1, sizeof(tag_elem_t)); el->name = strdup(tag->name); - el->tag = neu_tag_dup(tag); + el->tag = neu_tag_dup(tag); /* 复制标签内容 */ + /* 添加到哈希表 */ HASH_ADD_STR(group->tags, name, el); - update_timestamp(group); + update_timestamp(group); /* 更新组时间戳 */ pthread_mutex_unlock(&group->mtx); return 0; } +/** + * @brief 更新组中的标签 + * + * @param group 组实例 + * @param tag 包含更新信息的标签 + * @return 成功返回NEU_ERR_SUCCESS,未找到标签返回NEU_ERR_TAG_NOT_EXIST + */ int neu_group_update_tag(neu_group_t *group, const neu_datatag_t *tag) { tag_elem_t *el = NULL; int ret = NEU_ERR_TAG_NOT_EXIST; pthread_mutex_lock(&group->mtx); + /* 查找要更新的标签 */ HASH_FIND_STR(group->tags, tag->name, el); if (el != NULL) { - neu_tag_copy(el->tag, tag); + neu_tag_copy(el->tag, tag); /* 复制新的标签内容 */ - update_timestamp(group); + update_timestamp(group); /* 更新组时间戳 */ ret = NEU_ERR_SUCCESS; } pthread_mutex_unlock(&group->mtx); @@ -160,20 +227,30 @@ int neu_group_update_tag(neu_group_t *group, const neu_datatag_t *tag) return ret; } +/** + * @brief 从组中删除标签 + * + * @param group 组实例 + * @param tag_name 要删除的标签名称 + * @return 成功返回NEU_ERR_SUCCESS,未找到标签返回NEU_ERR_TAG_NOT_EXIST + */ int neu_group_del_tag(neu_group_t *group, const char *tag_name) { tag_elem_t *el = NULL; int ret = NEU_ERR_TAG_NOT_EXIST; pthread_mutex_lock(&group->mtx); + /* 查找要删除的标签 */ HASH_FIND_STR(group->tags, tag_name, el); if (el != NULL) { + /* 从哈希表中删除 */ HASH_DEL(group->tags, el); + /* 释放资源 */ free(el->name); neu_tag_free(el->tag); free(el); - update_timestamp(group); + update_timestamp(group); /* 更新组时间戳 */ ret = NEU_ERR_SUCCESS; } pthread_mutex_unlock(&group->mtx); @@ -181,28 +258,46 @@ int neu_group_del_tag(neu_group_t *group, const char *tag_name) return ret; } +/** + * @brief 获取组中的所有标签 + * + * @param group 组实例 + * @return 包含所有标签的数组 + */ UT_array *neu_group_get_tag(neu_group_t *group) { UT_array *array = NULL; pthread_mutex_lock(&group->mtx); - array = to_array(group->tags); + array = to_array(group->tags); /* 将哈希表转换为数组 */ pthread_mutex_unlock(&group->mtx); return array; } +/** + * @brief 查询组中包含指定名称的标签 + * + * 根据给定的名称字符串查询标签,匹配所有包含该字符串的标签名 + * + * @param group 组实例 + * @param name 要查询的名称字符串 + * @return 匹配的标签数组 + */ UT_array *neu_group_query_tag(neu_group_t *group, const char *name) { tag_elem_t *el = NULL, *tmp = NULL; - UT_array * array = NULL; + UT_array *array = NULL; pthread_mutex_lock(&group->mtx); + /* 创建新的标签数组 */ utarray_new(array, neu_tag_get_icd()); + /* 遍历所有标签 */ HASH_ITER(hh, group->tags, el, tmp) { + /* 检查标签名是否包含查询字符串 */ if (strstr(el->tag->name, name) != NULL) { - utarray_push_back(array, el->tag); + utarray_push_back(array, el->tag); /* 添加匹配的标签 */ } } pthread_mutex_unlock(&group->mtx); @@ -210,57 +305,110 @@ UT_array *neu_group_query_tag(neu_group_t *group, const char *name) return array; } +/** + * @brief 获取组中所有可读标签 + * + * 获取具有读取、订阅或静态属性的标签 + * + * @param group 组实例 + * @return 可读标签数组 + */ UT_array *neu_group_get_read_tag(neu_group_t *group) { UT_array *array = NULL; pthread_mutex_lock(&group->mtx); - array = to_read_array(group->tags); + array = to_read_array(group->tags); /* 获取可读标签 */ pthread_mutex_unlock(&group->mtx); return array; } +/** + * @brief 获取组中标签的数量 + * + * @param group 组实例 + * @return 标签数量 + */ uint16_t neu_group_tag_size(const neu_group_t *group) { uint16_t size = 0; + /* 获取哈希表中的元素数量 */ size = HASH_COUNT(group->tags); return size; } +/** + * @brief 在组中查找特定标签 + * + * 根据标签名精确查找标签,并返回其副本 + * + * @param group 组实例 + * @param tag 要查找的标签名 + * @return 找到则返回标签副本,否则返回NULL + */ neu_datatag_t *neu_group_find_tag(neu_group_t *group, const char *tag) { - tag_elem_t * find = NULL; + tag_elem_t *find = NULL; neu_datatag_t *result = NULL; pthread_mutex_lock(&group->mtx); + /* 根据名称精确查找标签 */ HASH_FIND_STR(group->tags, tag, find); if (find != NULL) { - result = neu_tag_dup(find->tag); + result = neu_tag_dup(find->tag); /* 创建标签副本 */ } pthread_mutex_unlock(&group->mtx); return result; } +/** + * @brief 将组中的标签分为静态标签和其他标签 + * + * @param group 组实例 + * @param static_tags 用于返回静态标签数组 + * @param other_tags 用于返回其他标签数组 + */ void neu_group_split_static_tags(neu_group_t *group, UT_array **static_tags, UT_array **other_tags) { + /* 调用内部函数分离静态标签和其他标签 */ return split_static_array(group->tags, static_tags, other_tags); } +/** + * @brief 检测组是否发生变化并执行回调 + * + * 比较组的时间戳与给定时间戳,如有变化则执行回调函数 + * + * @param group 组实例 + * @param timestamp 比较的时间戳 + * @param arg 传递给回调函数的参数 + * @param fn 回调函数 + */ void neu_group_change_test(neu_group_t *group, int64_t timestamp, void *arg, neu_group_change_fn fn) { + /* 检查时间戳是否变化 */ if (group->timestamp != timestamp) { UT_array *static_tags = NULL, *other_tags = NULL; + /* 分离静态标签和其他标签 */ split_static_array(group->tags, &static_tags, &other_tags); + /* 执行回调函数 */ fn(arg, group->timestamp, static_tags, other_tags, group->interval); } } +/** + * @brief 检查组是否已变更 + * + * @param group 组实例 + * @param timestamp 比较的时间戳 + * @return 如果组时间戳与给定时间戳不同,返回true + */ bool neu_group_is_change(neu_group_t *group, int64_t timestamp) { bool change = false; @@ -270,57 +418,102 @@ bool neu_group_is_change(neu_group_t *group, int64_t timestamp) return change; } +/** + * @brief 更新组的时间戳为当前时间 + * + * @param group 组实例 + */ static void update_timestamp(neu_group_t *group) { struct timeval tv = { 0 }; + /* 获取当前时间 */ gettimeofday(&tv, NULL); + /* 计算微秒级时间戳 */ group->timestamp = (int64_t) tv.tv_sec * 1000 * 1000 + (int64_t) tv.tv_usec; } +/** + * @brief 将标签哈希表转换为数组 + * + * @param tags 标签哈希表 + * @return 包含所有标签的数组 + */ static UT_array *to_array(tag_elem_t *tags) { tag_elem_t *el = NULL, *tmp = NULL; - UT_array * array = NULL; + UT_array *array = NULL; + /* 创建新数组 */ utarray_new(array, neu_tag_get_icd()); - HASH_ITER(hh, tags, el, tmp) { utarray_push_back(array, el->tag); } + /* 遍历哈希表并添加到数组 */ + HASH_ITER(hh, tags, el, tmp) + { + utarray_push_back(array, el->tag); + } return array; } +/** + * @brief 提取可读标签到数组 + * + * 获取具有读取、订阅或静态属性的标签 + * + * @param tags 标签哈希表 + * @return 可读标签数组 + */ static UT_array *to_read_array(tag_elem_t *tags) { tag_elem_t *el = NULL, *tmp = NULL; - UT_array * array = NULL; + UT_array *array = NULL; + /* 创建新数组 */ utarray_new(array, neu_tag_get_icd()); + /* 遍历哈希表 */ HASH_ITER(hh, tags, el, tmp) { + /* 检查标签是否具有可读属性 */ if (neu_tag_attribute_test(el->tag, NEU_ATTRIBUTE_READ) || neu_tag_attribute_test(el->tag, NEU_ATTRIBUTE_SUBSCRIBE) || neu_tag_attribute_test(el->tag, NEU_ATTRIBUTE_STATIC)) { - utarray_push_back(array, el->tag); + utarray_push_back(array, el->tag); /* 添加到数组 */ } } return array; } +/** + * @brief 将标签分为静态标签和其他标签 + * + * 静态标签具有NEU_ATTRIBUTE_STATIC属性 + * 其他标签包括具有NEU_ATTRIBUTE_SUBSCRIBE或NEU_ATTRIBUTE_READ属性的标签 + * + * @param tags 标签哈希表 + * @param static_tags 用于返回静态标签数组 + * @param other_tags 用于返回其他标签数组 + */ static void split_static_array(tag_elem_t *tags, UT_array **static_tags, UT_array **other_tags) { tag_elem_t *el = NULL, *tmp = NULL; + /* 创建两个新数组 */ utarray_new(*static_tags, neu_tag_get_icd()); utarray_new(*other_tags, neu_tag_get_icd()); + + /* 遍历标签哈希表 */ HASH_ITER(hh, tags, el, tmp) { + /* 根据标签属性分类 */ if (neu_tag_attribute_test(el->tag, NEU_ATTRIBUTE_STATIC)) { + /* 静态标签 */ utarray_push_back(*static_tags, el->tag); } else if (neu_tag_attribute_test(el->tag, NEU_ATTRIBUTE_SUBSCRIBE) || neu_tag_attribute_test(el->tag, NEU_ATTRIBUTE_READ)) { + /* 可订阅或可读标签 */ utarray_push_back(*other_tags, el->tag); } } diff --git a/src/base/metrics.c b/src/base/metrics.c index 2e9a56503..99f38aa0b 100644 --- a/src/base/metrics.c +++ b/src/base/metrics.c @@ -29,15 +29,32 @@ #include "utils/log.h" #include "utils/time.h" +/** + * @brief 度量指标的读写锁,保护全局度量数据的并发访问 + */ static pthread_rwlock_t g_metrics_mtx_ = PTHREAD_RWLOCK_INITIALIZER; -static neu_metrics_t g_metrics_; -static uint64_t g_start_ts_; +/** + * @brief 全局度量指标结构体,存储各种系统和应用指标数据 + */ +static neu_metrics_t g_metrics_; + +/** + * @brief 应用启动时间戳,用于计算运行时间 + */ +static uint64_t g_start_ts_; + +/** + * @brief 获取操作系统信息 + * + * 执行系统命令获取操作系统发行版和内核版本信息 + */ static void find_os_info() { + /* 构建命令:从/etc/os-release获取系统信息,如果不存在则使用uname */ const char *cmd = "if [ -f /etc/os-release ]; then . /etc/os-release;" "echo $NAME $VERSION_ID; else uname -s; fi; uname -r"; - FILE *f = popen(cmd, "r"); + FILE *f = popen(cmd, "r"); if (NULL == f) { nlog_error("popen command fail"); @@ -46,33 +63,48 @@ static void find_os_info() char buf[64] = {}; + /* 获取发行版信息 */ if (NULL == fgets(buf, sizeof(buf), f)) { nlog_error("no command output"); pclose(f); return; } + /* 移除换行符并复制到度量结构体中 */ buf[strcspn(buf, "\n")] = 0; strncpy(g_metrics_.distro, buf, sizeof(g_metrics_.distro)); - g_metrics_.distro[sizeof(g_metrics_.distro) - 1] = 0; + g_metrics_.distro[sizeof(g_metrics_.distro) - 1] = + 0; /* 确保字符串以NULL结尾 */ + /* 获取内核版本信息 */ if (NULL == fgets(buf, sizeof(buf), f)) { nlog_error("no command output"); pclose(f); return; } + /* 移除换行符并复制到度量结构体中 */ buf[strcspn(buf, "\n")] = 0; strncpy(g_metrics_.kernel, buf, sizeof(g_metrics_.kernel)); - g_metrics_.kernel[sizeof(g_metrics_.kernel) - 1] = 0; + g_metrics_.kernel[sizeof(g_metrics_.kernel) - 1] = + 0; /* 确保字符串以NULL结尾 */ pclose(f); } +/** + * @brief 解析内存字段值 + * + * 通过执行free命令并提取特定列的值来获取内存使用情况 + * + * @param col 要提取的free命令输出的列号 + * @return 提取的内存值(字节) + */ static size_t parse_memory_fields(int col) { - FILE * f = NULL; + FILE *f = NULL; char buf[64] = {}; size_t val = 0; + /* 构建命令:获取free命令输出的特定列(以字节为单位) */ sprintf(buf, "free -b | awk 'NR==2 {print $%i}'", col); f = popen(buf, "r"); @@ -81,6 +113,7 @@ static size_t parse_memory_fields(int col) return 0; } + /* 解析命令输出的数值 */ if (NULL != fgets(buf, sizeof(buf), f)) { val = atoll(buf); } else { @@ -91,23 +124,41 @@ static size_t parse_memory_fields(int col) return val; } +/** + * @brief 获取系统总内存大小 + * + * @return 总内存大小(字节) + */ static inline size_t memory_total() { - return parse_memory_fields(2); + return parse_memory_fields(2); /* free命令输出的第2列是总内存 */ } +/** + * @brief 获取系统已使用内存大小 + * + * @return 已使用内存大小(字节) + */ static inline size_t memory_used() { - return parse_memory_fields(3); + return parse_memory_fields(3); /* free命令输出的第3列是已用内存 */ } +/** + * @brief 获取Neuron进程使用的内存 + * + * 通过ps命令获取当前进程的内存使用情况 + * + * @return Neuron进程使用的内存大小(字节) + */ static inline size_t neuron_memory_used() { - FILE * f = NULL; + FILE *f = NULL; char buf[32] = {}; size_t val = 0; - pid_t pid = getpid(); + pid_t pid = getpid(); /* 获取当前进程ID */ + /* 构建命令:获取当前进程的RSS值(驻留集大小) */ sprintf(buf, "ps -o rss= %ld", (long) pid); f = popen(buf, "r"); @@ -116,6 +167,7 @@ static inline size_t neuron_memory_used() return 0; } + /* 解析命令输出的RSS值 */ if (NULL != fgets(buf, sizeof(buf), f)) { val = atoll(buf); } else { @@ -123,47 +175,76 @@ static inline size_t neuron_memory_used() } pclose(f); - return val * 1024; + return val * 1024; /* ps命令输出的RSS单位是KB,转换为字节 */ } +/** + * @brief 获取系统缓存大小 + * + * @return 缓存内存大小(字节) + */ static inline size_t memory_cache() { - return parse_memory_fields(6); + return parse_memory_fields(6); /* free命令输出的第6列是缓存大小 */ } +/** + * @brief 获取当前目录所在磁盘的使用情况 + * + * 通过statvfs系统调用获取磁盘总大小、已使用和可用空间 + * + * @param size_p 用于返回磁盘总大小(GB) + * @param used_p 用于返回已使用空间(GB) + * @param avail_p 用于返回可用空间(GB) + * @return 成功返回0,失败返回-1 + */ static inline int disk_usage(size_t *size_p, size_t *used_p, size_t *avail_p) { struct statvfs buf = {}; + /* 获取当前目录的文件系统信息 */ if (0 != statvfs(".", &buf)) { return -1; } + /* 计算磁盘大小,单位转换为GB */ *size_p = (double) buf.f_frsize * buf.f_blocks / (1 << 30); *used_p = (double) buf.f_frsize * (buf.f_blocks - buf.f_bfree) / (1 << 30); *avail_p = (double) buf.f_frsize * buf.f_bavail / (1 << 30); return 0; } +/** + * @brief 获取CPU使用率 + * + * 通过两次读取/proc/stat中的CPU时间数据,计算CPU使用率 + * + * @return CPU使用率百分比(0-100) + */ static unsigned cpu_usage() { - int ret = 0; + int ret = 0; + /* 第一次采样的CPU时间数据 */ unsigned long long user1 = 0, nice1 = 0, sys1 = 0, idle1 = 0, iowait1 = 0, irq1 = 0, softirq1 = 0; + /* 第二次采样的CPU时间数据 */ unsigned long long user2 = 0, nice2 = 0, sys2 = 0, idle2 = 0, iowait2 = 0, irq2 = 0, softirq2 = 0; unsigned long long work = 0, total = 0; - struct timespec tv = { + /* 两次采样间的时间间隔(50毫秒) */ + struct timespec tv = { .tv_sec = 0, .tv_nsec = 50000000, }; FILE *f = NULL; + /* 打开/proc/stat文件 */ f = fopen("/proc/stat", "r"); if (NULL == f) { nlog_error("open /proc/stat fail"); return 0; } + /* 第一次读取CPU时间数据 */ ret = fscanf(f, "cpu %llu %llu %llu %llu %llu %llu %llu", &user1, &nice1, &sys1, &idle1, &iowait1, &irq1, &softirq1); if (7 != ret) { @@ -171,9 +252,12 @@ static unsigned cpu_usage() return 0; } + /* 休眠50毫秒,等待CPU状态变化 */ nanosleep(&tv, NULL); + /* 重新定位文件指针到文件开头 */ rewind(f); + /* 第二次读取CPU时间数据 */ ret = fscanf(f, "cpu %llu %llu %llu %llu %llu %llu %llu", &user2, &nice2, &sys2, &idle2, &iowait2, &irq2, &softirq2); if (7 != ret) { @@ -182,23 +266,36 @@ static unsigned cpu_usage() } fclose(f); - work = (user2 - user1) + (nice2 - nice1) + (sys2 - sys1); + /* 计算工作时间(user+nice+system)的变化量 */ + work = (user2 - user1) + (nice2 - nice1) + (sys2 - sys1); + /* 计算总时间的变化量 */ total = work + (idle2 - idle1) + (iowait2 - iowait1) + (irq2 - irq1) + (softirq2 - softirq1); + /* 计算CPU使用率百分比,并乘以CPU核心数 */ return (double) work / total * 100.0 * sysconf(_SC_NPROCESSORS_CONF); } +/** + * @brief 检查指定目录中是否存在以特定前缀开头的核心转储文件 + * + * @param dir 要检查的目录路径 + * @param prefix 核心转储文件名前缀 + * @return 如果找到核心转储文件则返回true,否则返回false + */ static bool has_core_dump_in_dir(const char *dir, const char *prefix) { + /* 打开指定目录 */ DIR *dp = opendir(dir); if (dp == NULL) { return false; } + /* 遍历目录中的文件 */ struct dirent *de; bool found = false; while ((de = readdir(dp)) != NULL) { + /* 检查文件名是否以指定前缀开头 */ if (strncmp(prefix, de->d_name, strlen(prefix)) == 0) { found = true; break; @@ -209,12 +306,22 @@ static bool has_core_dump_in_dir(const char *dir, const char *prefix) return found; } +/** + * @brief 从核心转储模式字符串中提取目录路径 + * + * 从/proc/sys/kernel/core_pattern文件内容中提取核心转储文件的目录路径 + * + * @param core_pattern 核心转储模式字符串 + * @return 提取的目录路径,如果没有目录路径则返回NULL + */ static char *get_core_dir(const char *core_pattern) { + /* 查找最后一个斜杠位置,用于分离目录路径 */ char *last_slash = strrchr(core_pattern, '/'); if (last_slash != NULL) { static char core_dir[256]; ptrdiff_t path_length = last_slash - core_pattern + 1; + /* 复制目录部分到core_dir */ strncpy(core_dir, core_pattern, path_length); core_dir[path_length] = '\0'; return core_dir; @@ -222,17 +329,27 @@ static char *get_core_dir(const char *core_pattern) return NULL; } +/** + * @brief 检查系统中是否存在Neuron的核心转储文件 + * + * 首先检查当前目录的core文件夹,然后根据系统核心转储配置检查其他可能的位置 + * + * @return 如果找到核心转储文件则返回true,否则返回false + */ static bool has_core_dumps() { + /* 首先检查当前目录下的core目录 */ if (has_core_dump_in_dir("core", "core-neuron")) { return true; } + /* 读取系统核心转储模式配置 */ FILE *fp = fopen("/proc/sys/kernel/core_pattern", "r"); if (fp == NULL) { return false; } + /* 读取core_pattern内容 */ char core_pattern[256]; if (fgets(core_pattern, sizeof(core_pattern), fp) == NULL) { fclose(fp); @@ -240,19 +357,33 @@ static bool has_core_dumps() } fclose(fp); + /* 确定核心转储文件的目录 + * 如果core_pattern以'|'开头,表示使用管道处理核心转储, + * 通常Ubuntu系统会将核心转储存储在/var/crash/目录 + */ char *core_dir = (core_pattern[0] == '|') ? "/var/crash/" : get_core_dir(core_pattern); if (core_dir == NULL) { return false; } + /* 在核心转储目录中查找neuron的核心转储文件 */ return has_core_dump_in_dir(core_dir, "core-neuron"); } +/** + * @brief 注销度量指标条目 + * + * 从全局度量指标哈希表中注销一个条目,当引用计数为0时删除该条目 + * + * @param name 要注销的度量指标名称 + */ static inline void metrics_unregister_entry(const char *name) { neu_metric_entry_t *e = NULL; + /* 在哈希表中查找指定名称的条目 */ HASH_FIND_STR(g_metrics_.registered_metrics, name, e); + /* 递减引用计数,当计数为0时删除条目 */ if (0 == --e->value) { HASH_DEL(g_metrics_.registered_metrics, e); nlog_notice("del entry:%s", e->name); @@ -260,13 +391,25 @@ static inline void metrics_unregister_entry(const char *name) } } +/** + * @brief 向度量指标集合添加一个新的度量指标条目 + * + * @param entries 度量指标集合的哈希表指针 + * @param name 度量指标名称 + * @param help 度量指标的帮助说明文本 + * @param type 度量指标类型(计数器、仪表等) + * @param init 度量指标的初始值 + * @return 成功返回0,指标已存在返回1,失败返回-1 + */ int neu_metric_entries_add(neu_metric_entry_t **entries, const char *name, const char *help, neu_metric_type_e type, uint64_t init) { neu_metric_entry_t *entry = NULL; + /* 检查是否已存在同名的度量指标 */ HASH_FIND_STR(*entries, name, entry); if (NULL != entry) { + /* 如果已存在但类型或帮助文本不匹配,则报错 */ if (entry->type != type || (0 != strcmp(entry->help, help))) { nlog_error("metric entry %s(%d, %s) conflicts with (%d, %s)", name, entry->type, entry->help, type, help); @@ -275,34 +418,48 @@ int neu_metric_entries_add(neu_metric_entry_t **entries, const char *name, return 1; } + /* 创建新的度量指标条目 */ entry = calloc(1, sizeof(*entry)); if (NULL == entry) { return -1; } + /* 根据指标类型进行初始化 */ if (NEU_METRIC_TYPE_ROLLING_COUNTER == type) { - // only allocate rolling counter for nonzero time span + // 只为非零时间跨度分配滚动计数器 if (init > 0 && NULL == (entry->rcnt = neu_rolling_counter_new(init))) { free(entry); return -1; } } else { + /* 对于其他类型,直接设置初始值 */ entry->value = init; } + /* 设置度量指标的属性 */ entry->name = name; entry->type = type; entry->help = help; + /* 将新的度量指标添加到哈希表 */ HASH_ADD_STR(*entries, name, entry); return 0; } +/** + * @brief 初始化度量指标系统 + * + * 初始化全局度量指标相关变量,获取系统信息和内存总量 + */ void neu_metrics_init() { + /* 加写锁,确保初始化过程的线程安全 */ pthread_rwlock_wrlock(&g_metrics_mtx_); if (0 == g_start_ts_) { + /* 记录启动时间戳 */ g_start_ts_ = neu_time_ms(); + /* 获取操作系统信息 */ find_os_info(); + /* 获取系统内存总量 */ g_metrics_.mem_total_bytes = memory_total(); } pthread_rwlock_unlock(&g_metrics_mtx_); diff --git a/src/base/tag.c b/src/base/tag.c index 2f30b2c3b..dc9acf168 100644 --- a/src/base/tag.c +++ b/src/base/tag.c @@ -26,98 +26,171 @@ config_ **/ #include "errcodes.h" #include "tag.h" +/** + * @brief 从标签的meta字段获取静态值指针 + * + * @param tag 标签结构体 + * @param out 输出的值指针 + */ #define GET_STATIC_VALUE_PTR(tag, out) \ do { \ memcpy(&(out), (tag)->meta, sizeof(neu_value_u *)); \ } while (0) +/** + * @brief 设置标签的静态值指针到meta字段 + * + * @param tag 标签结构体 + * @param ptr 值指针 + */ #define SET_STATIC_VALUE_PTR(tag, ptr) \ do { \ memcpy((tag)->meta, &(ptr), sizeof(neu_value_u *)); \ } while (0) +/** + * @brief 复制标签数组元素 + * + * 用于UT_array的复制回调函数,深度复制标签结构体 + * + * @param _dst 目标指针 + * @param _src 源指针 + */ static void tag_array_copy(void *_dst, const void *_src) { neu_datatag_t *dst = (neu_datatag_t *) _dst; neu_datatag_t *src = (neu_datatag_t *) _src; - dst->type = src->type; - dst->attribute = src->attribute; - dst->precision = src->precision; - dst->decimal = src->decimal; - dst->option = src->option; - dst->address = strdup(src->address); - dst->name = strdup(src->name); - dst->description = strdup(src->description); - + /* 复制基本属性 */ + dst->type = src->type; /* 数据类型 */ + dst->attribute = src->attribute; /* 标签属性 */ + dst->precision = src->precision; /* 精度 */ + dst->decimal = src->decimal; /* 小数位数 */ + dst->option = src->option; /* 选项 */ + dst->address = strdup(src->address); /* 复制地址字符串 */ + dst->name = strdup(src->name); /* 复制名称字符串 */ + dst->description = strdup(src->description); /* 复制描述字符串 */ + + /* 处理静态标签特有的值 */ if (NEU_ATTRIBUTE_STATIC & src->attribute) { neu_value_u *dst_val = NULL, *src_val = NULL; - GET_STATIC_VALUE_PTR(src, src_val); + GET_STATIC_VALUE_PTR(src, src_val); /* 获取源标签的静态值指针 */ if (src_val && (dst_val = calloc(1, sizeof(*dst_val)))) { + /* 分配并复制静态值内存 */ memcpy(dst_val, src_val, sizeof(*dst_val)); - SET_STATIC_VALUE_PTR(dst, dst_val); + SET_STATIC_VALUE_PTR(dst, dst_val); /* 设置目标标签的静态值指针 */ } else { + /* 内存分配失败或源值为空,清零meta字段 */ memset(dst->meta, 0, sizeof(dst->meta)); } } else { + /* 非静态标签,直接复制meta字段 */ memcpy(dst->meta, src->meta, sizeof(src->meta)); } } +/** + * @brief 释放标签数组元素 + * + * 用于UT_array的释放回调函数,释放标签结构体中的动态分配内存 + * + * @param _elt 要释放的元素指针 + */ static void tag_array_free(void *_elt) { neu_datatag_t *elt = (neu_datatag_t *) _elt; + /* 释放字符串字段 */ free(elt->name); free(elt->address); free(elt->description); + /* 处理静态标签特有的值 */ if (NEU_ATTRIBUTE_STATIC & elt->attribute) { neu_value_u *cur = NULL; - GET_STATIC_VALUE_PTR(elt, cur); - free(cur); - memset(elt->meta, 0, sizeof(elt->meta)); + GET_STATIC_VALUE_PTR(elt, cur); /* 获取静态值指针 */ + free(cur); /* 释放静态值内存 */ + memset(elt->meta, 0, sizeof(elt->meta)); /* 清零meta字段 */ } } +/** + * @brief 标签数组的UT_array接口定义 + */ static UT_icd tag_icd = { sizeof(neu_datatag_t), NULL, tag_array_copy, tag_array_free }; +/** + * @brief 获取标签数组的UT_array接口定义 + * + * @return 标签数组的UT_icd指针 + */ UT_icd *neu_tag_get_icd() { return &tag_icd; } +/** + * @brief 复制创建一个新的标签 + * + * @param tag 源标签 + * @return 新创建的标签副本 + */ neu_datatag_t *neu_tag_dup(const neu_datatag_t *tag) { neu_datatag_t *new = calloc(1, sizeof(*new)); - tag_array_copy(new, tag); + tag_array_copy(new, tag); /* 使用数组复制函数复制内容 */ return new; } +/** + * @brief 将一个标签的内容复制到另一个已存在的标签 + * + * 先释放目标标签中的动态资源,再复制源标签的内容 + * + * @param tag 目标标签 + * @param other 源标签 + */ void neu_tag_copy(neu_datatag_t *tag, const neu_datatag_t *other) { if (tag) { - tag_array_free(tag); - tag_array_copy(tag, other); + tag_array_free(tag); /* 释放原有资源 */ + tag_array_copy(tag, other); /* 复制新内容 */ } } +/** + * @brief 清理标签内部资源但不释放标签结构本身 + * + * @param tag 要清理的标签 + */ void neu_tag_fini(neu_datatag_t *tag) { if (tag) { - tag_array_free(tag); + tag_array_free(tag); /* 释放标签内部资源 */ } } +/** + * @brief 释放标签及其资源 + * + * @param tag 要释放的标签 + */ void neu_tag_free(neu_datatag_t *tag) { if (tag) { - tag_array_free(tag); - free(tag); + tag_array_free(tag); /* 释放标签内部资源 */ + free(tag); /* 释放标签结构本身 */ } } +/** + * @brief 在字符串中查找最后一个指定字符的位置 + * + * @param str 要搜索的字符串 + * @param character 要查找的字符 + * @return 找到则返回指向该字符的指针,未找到返回NULL + */ static char *find_last_character(char *str, char character) { char *find = strchr(str, character); @@ -131,18 +204,29 @@ static char *find_last_character(char *str, char character) return ret; } -int neu_datatag_parse_addr_option(const neu_datatag_t * datatag, +/** + * @brief 解析标签地址中的选项参数 + * + * 从标签地址字符串中提取类型特定的选项参数 + * + * @param datatag 数据标签 + * @param option 用于存储解析结果的选项结构 + * @return 成功返回0,失败返回-1 + */ +int neu_datatag_parse_addr_option(const neu_datatag_t *datatag, neu_datatag_addr_option_u *option) { int ret = 0; switch (datatag->type) { case NEU_TYPE_BYTES: { + /* 查找字节类型地址中的长度选项(格式如 "地址.长度") */ char *op = find_last_character(datatag->address, '.'); if (op == NULL) { - ret = -1; + ret = -1; /* 未找到分隔符 */ } else { + /* 解析字节长度 */ int n = sscanf(op, ".%hhd", &option->bytes.length); if (n != 1 || option->string.length <= 0) { ret = -1; @@ -784,7 +868,7 @@ neu_datatag_t *neu_ndriver_tag_dup(const neu_ndriver_tag_t *tag) return new; } -void neu_ndriver_tag_copy(neu_ndriver_tag_t * tag, +void neu_ndriver_tag_copy(neu_ndriver_tag_t *tag, const neu_ndriver_tag_t *other) { ndriver_tag_dtor(tag); diff --git a/src/base/tag_sort.c b/src/base/tag_sort.c index 5118a5b52..3ca9c85a3 100644 --- a/src/base/tag_sort.c +++ b/src/base/tag_sort.c @@ -20,18 +20,47 @@ #include "tag_sort.h" +/** + * @brief 将UT_array转换为双向链表 + * + * @param tags 标签数组 + * @return 转换后的链表头指针 + */ static neu_tag_sort_elem_t *array_to_list(UT_array *tags); -static void tag_sort(neu_tag_sort_result_t *result, void *tag, - neu_tag_sort_fn fn, UT_icd icd); +/** + * @brief 将标签添加到排序结果中 + * + * @param result 排序结果结构体 + * @param tag 要添加的标签 + * @param fn 排序函数 + * @param icd UT数组的元素描述符 + */ +static void tag_sort(neu_tag_sort_result_t *result, void *tag, + neu_tag_sort_fn fn, UT_icd icd); + +/** + * @brief 对标签进行分组排序 + * + * 该函数将输入的标签数组按照提供的排序函数和比较函数进行分组和排序 + * + * @param tags 要排序的标签数组 + * @param sort 排序函数,用于判断标签是否属于同一组 + * @param cmp 比较函数,用于确定标签的顺序 + * @return 排序结果结构体指针 + */ neu_tag_sort_result_t *neu_tag_sort(UT_array *tags, neu_tag_sort_fn sort, neu_tag_sort_cmp cmp) { + /* 分配排序结果结构体 */ neu_tag_sort_result_t *result = calloc(1, sizeof(neu_tag_sort_result_t)); - neu_tag_sort_elem_t * elt = NULL, *tmp = NULL; - neu_tag_sort_elem_t * head = array_to_list(tags); + neu_tag_sort_elem_t *elt = NULL, *tmp = NULL; + /* 将数组转换为链表以便排序 */ + neu_tag_sort_elem_t *head = array_to_list(tags); + /* 使用提供的比较函数对链表进行排序 */ DL_SORT(head, cmp); + /* 遍历排序后的链表,将每个标签分组添加到结果中 */ DL_FOREACH_SAFE(head, elt, tmp) { tag_sort(result, elt->tag, sort, tags->icd); @@ -42,38 +71,69 @@ neu_tag_sort_result_t *neu_tag_sort(UT_array *tags, neu_tag_sort_fn sort, return result; } +/** + * @brief 释放排序结果结构体资源 + * + * @param result 要释放的排序结果结构体指针 + */ void neu_tag_sort_free(neu_tag_sort_result_t *result) { + /* 释放每个分组中的标签数组 */ for (uint16_t i = 0; i < result->n_sort; i++) { utarray_free(result->sorts[i].tags); } + /* 释放分组数组和结果结构体 */ free(result->sorts); free(result); } +/** + * @brief 将UT_array转换为双向链表 + * + * 此函数将UT_array中的标签转换为双向链表,以便后续排序 + * + * @param tags 标签数组 + * @return 转换后的链表头指针 + */ static neu_tag_sort_elem_t *array_to_list(UT_array *tags) { neu_tag_sort_elem_t *head = NULL, *tmp = NULL; + /* 遍历标签数组,为每个标签创建链表节点 */ for (void **tag = utarray_front(tags); tag != NULL; tag = utarray_next(tags, tag)) { + /* 为节点分配内存 */ tmp = calloc(1, sizeof(neu_tag_sort_elem_t)); tmp->tag = *tag; + /* 将节点添加到链表末尾 */ DL_APPEND(head, tmp); } return head; } +/** + * @brief 将标签添加到排序结果中 + * + * 该函数尝试将标签添加到已有的分组中,如果没有合适的分组则创建新分组 + * + * @param result 排序结果结构体 + * @param tag 要添加的标签 + * @param fn 排序函数,用于判断标签是否属于同一组 + * @param icd UT数组的元素描述符 + */ static void tag_sort(neu_tag_sort_result_t *result, void *tag, neu_tag_sort_fn fn, UT_icd icd) { bool sorted = false; + /* 尝试将标签添加到已有的分组中 */ for (uint16_t i = 0; i < result->n_sort; i++) { + /* 使用排序函数判断标签是否属于当前分组 */ if (fn(&result->sorts[i], *(void **) utarray_back(result->sorts[i].tags), tag)) { + /* 将标签添加到当前分组 */ utarray_push_back(result->sorts[i].tags, &tag); result->sorts[i].info.size += 1; sorted = true; @@ -81,15 +141,20 @@ static void tag_sort(neu_tag_sort_result_t *result, void *tag, } } + /* 如果标签不属于任何已有分组,则创建新分组 */ if (!sorted) { + /* 增加分组计数 */ result->n_sort += 1; + /* 重新分配分组数组 */ result->sorts = realloc(result->sorts, sizeof(neu_tag_sort_t) * result->n_sort); + /* 初始化新分组 */ memset(&result->sorts[result->n_sort - 1], 0, sizeof(neu_tag_sort_t)); utarray_new(result->sorts[result->n_sort - 1].tags, &icd); utarray_push_back(result->sorts[result->n_sort - 1].tags, &tag); result->sorts[result->n_sort - 1].info.size = 1; + /* 调用排序函数初始化分组信息 */ fn(&result->sorts[result->n_sort - 1], *(void **) utarray_back(result->sorts[result->n_sort - 1].tags), tag); diff --git a/src/base/template.c b/src/base/template.c index f76c19bfe..3fc9d0715 100644 --- a/src/base/template.c +++ b/src/base/template.c @@ -24,37 +24,62 @@ #include "template.h" +/** + * @brief 组条目结构体 + * + * 用于在模板内部管理组 + */ typedef struct { - neu_group_t * group; - UT_hash_handle hh; + neu_group_t *group; /**< 指向组对象的指针 */ + UT_hash_handle hh; /**< UT哈希处理结构,用于哈希表实现 */ } group_entry_t; +/** + * @brief 模板结构体 + * + * 模板是一个预定义的配置模式,可以包含多个组和标签 + */ struct neu_template_s { - char * name; - char * plugin; - neu_plugin_tag_validator_t tag_validator; - group_entry_t * groups; + char *name; /**< 模板名称 */ + char *plugin; /**< 关联的插件名称 */ + neu_plugin_tag_validator_t tag_validator; /**< 标签验证器函数指针 */ + group_entry_t *groups; /**< 组条目哈希表 */ }; +/** + * @brief 释放组条目资源 + * + * @param ent 要释放的组条目指针 + */ static inline void group_entry_free(group_entry_t *ent) { - neu_group_destroy(ent->group); - free(ent); + neu_group_destroy(ent->group); /* 销毁组对象 */ + free(ent); /* 释放条目内存 */ } +/** + * @brief 创建新的模板对象 + * + * @param name 模板名称 + * @param plugin 关联的插件名称 + * @return 成功返回模板指针,失败返回NULL + */ neu_template_t *neu_template_new(const char *name, const char *plugin) { + /* 分配模板结构内存 */ neu_template_t *tmpl = calloc(1, sizeof(*tmpl)); if (NULL == tmpl) { return NULL; } + /* 复制模板名称 */ tmpl->name = strdup(name); if (NULL == tmpl->name) { free(tmpl); return NULL; } + /* 复制插件名称 */ tmpl->plugin = strdup(plugin); if (NULL == tmpl->plugin) { free(tmpl->name); @@ -65,9 +90,15 @@ neu_template_t *neu_template_new(const char *name, const char *plugin) return tmpl; } +/** + * @brief 释放模板对象资源 + * + * @param tmpl 要释放的模板对象指针 + */ void neu_template_free(neu_template_t *tmpl) { if (tmpl) { + /* 释放所有组条目 */ group_entry_t *ent = NULL, *tmp = NULL; HASH_ITER(hh, tmpl->groups, ent, tmp) { @@ -75,126 +106,222 @@ void neu_template_free(neu_template_t *tmpl) group_entry_free(ent); } + /* 释放模板基本属性和结构体 */ free(tmpl->name); free(tmpl->plugin); free(tmpl); } } -void neu_template_set_tag_validator(neu_template_t * tmpl, +/** + * @brief 设置模板的标签验证器函数 + * + * @param tmpl 模板对象指针 + * @param validator 标签验证器函数指针 + */ +void neu_template_set_tag_validator(neu_template_t *tmpl, neu_plugin_tag_validator_t validator) { tmpl->tag_validator = validator; } +/** + * @brief 获取模板名称 + * + * @param tmpl 模板对象指针 + * @return 模板名称字符串 + */ const char *neu_template_name(const neu_template_t *tmpl) { return tmpl->name; } +/** + * @brief 获取模板关联的插件名称 + * + * @param tmpl 模板对象指针 + * @return 插件名称字符串 + */ const char *neu_template_plugin(const neu_template_t *tmpl) { return tmpl->plugin; } +/** + * @brief 获取模板中指定名称的组 + * + * @param tmpl 模板对象指针 + * @param group 组名称 + * @return 成功返回组指针,如果组不存在则返回NULL + */ neu_group_t *neu_template_get_group(const neu_template_t *tmpl, - const char * group) + const char *group) { + /* 在哈希表中查找对应名称的组 */ group_entry_t *ent = NULL; HASH_FIND(hh, tmpl->groups, group, strlen(group), ent); return ent ? ent->group : NULL; } +/** + * @brief 向模板中添加新组 + * + * @param tmpl 模板对象指针 + * @param group 组名称 + * @param interval 组轮询间隔(毫秒) + * @return 成功返回0,失败返回错误码 + * - NEU_ERR_GROUP_EXIST: 组已存在 + * - NEU_ERR_EINTERNAL: 内部错误(内存分配失败等) + */ int neu_template_add_group(neu_template_t *tmpl, const char *group, uint32_t interval) { group_entry_t *ent = NULL; + /* 检查组是否已存在 */ HASH_FIND(hh, tmpl->groups, group, strlen(group), ent); if (ent) { return NEU_ERR_GROUP_EXIST; } + /* 分配组条目内存 */ ent = calloc(1, sizeof(*ent)); if (NULL == ent) { return NEU_ERR_EINTERNAL; } + /* 创建新的组对象 */ ent->group = neu_group_new(group, interval); if (NULL == ent->group) { free(ent); return NEU_ERR_EINTERNAL; } + /* 将组条目添加到哈希表 */ HASH_ADD_KEYPTR(hh, tmpl->groups, neu_group_get_name(ent->group), strlen(group), ent); return 0; } +/** + * @brief 从模板中删除指定组 + * + * @param tmpl 模板对象指针 + * @param group 要删除的组名称 + * @return 成功返回0,失败返回错误码 + * - NEU_ERR_GROUP_NOT_EXIST: 组不存在 + */ int neu_template_del_group(neu_template_t *tmpl, const char *group) { + /* 在哈希表中查找指定组 */ group_entry_t *ent = NULL; HASH_FIND(hh, tmpl->groups, group, strlen(group), ent); if (NULL == ent) { return NEU_ERR_GROUP_NOT_EXIST; } + /* 从哈希表中删除组条目并释放资源 */ HASH_DEL(tmpl->groups, ent); group_entry_free(ent); return 0; } +/** + * @brief 更新模板中指定组的轮询间隔 + * + * @param tmpl 模板对象指针 + * @param group 组名称 + * @param interval 新的轮询间隔(毫秒) + * @return 成功返回0,失败返回错误码 + * - NEU_ERR_GROUP_NOT_EXIST: 组不存在 + */ int neu_template_update_group_interval(neu_template_t *tmpl, const char *group, uint32_t interval) { + /* 查找指定的组 */ group_entry_t *ent = NULL; HASH_FIND(hh, tmpl->groups, group, strlen(group), ent); if (NULL == ent) { return NEU_ERR_GROUP_NOT_EXIST; } + /* 更新组的轮询间隔 */ return neu_group_update(ent->group, interval); } +/** + * @brief 更新模板中指定组的名称 + * + * @param tmpl 模板对象指针 + * @param group 当前组名称 + * @param new_name 新的组名称 + * @return 成功返回0,失败返回错误码 + * - NEU_ERR_GROUP_EXIST: 新名称的组已存在 + * - NEU_ERR_GROUP_NOT_EXIST: 原组不存在 + */ int neu_template_update_group_name(neu_template_t *tmpl, const char *group, const char *new_name) { + /* 如果新旧名称相同,直接返回成功 */ if (0 == strcmp(group, new_name)) { return 0; } + /* 检查新名称是否已被占用 */ if (NULL != neu_template_get_group(tmpl, new_name)) { return NEU_ERR_GROUP_EXIST; } + /* 查找指定的组 */ group_entry_t *ent = NULL; HASH_FIND(hh, tmpl->groups, group, strlen(group), ent); if (NULL == ent) { return NEU_ERR_GROUP_NOT_EXIST; } + /* 从哈希表中移除组条目 */ HASH_DEL(tmpl->groups, ent); - int rv = neu_group_set_name(ent->group, new_name); + /* 设置组的新名称 */ + int rv = neu_group_set_name(ent->group, new_name); + /* 获取实际设置的组名称(可能经过处理) */ new_name = neu_group_get_name(ent->group); + /* 使用新名称将组条目重新添加到哈希表 */ HASH_ADD_KEYPTR(hh, tmpl->groups, new_name, strlen(new_name), ent); return rv; } +/** + * @brief 获取模板中的组数量 + * + * @param tmpl 模板对象指针 + * @return 模板中的组数量 + */ size_t neu_template_group_num(const neu_template_t *tmpl) { return HASH_COUNT(tmpl->groups); } +/** + * @brief 遍历模板中的所有组并对每个组执行回调函数 + * + * @param tmpl 模板对象指针 + * @param cb 对每个组执行的回调函数,函数签名为 int (*cb)(neu_group_t *group, + * void *data) + * @param data 传递给回调函数的用户数据 + * @return 成功返回0,如果回调函数返回非0值则终止遍历并返回该值 + */ int neu_template_for_each_group(neu_template_t *tmpl, int (*cb)(neu_group_t *group, void *data), void *data) { int rv = 0; group_entry_t *ent = NULL, *tmp = NULL; + /* 遍历哈希表中的所有组条目 */ HASH_ITER(hh, tmpl->groups, ent, tmp) { + /* 对每个组执行回调函数,如果返回非0值则中断遍历 */ if (0 != (rv = cb(ent->group, data))) { break; } @@ -203,45 +330,77 @@ int neu_template_for_each_group(neu_template_t *tmpl, return rv; } +/** + * @brief 向模板的指定组中添加标签 + * + * @param tmpl 模板对象指针 + * @param group 组名称 + * @param tag 要添加的标签对象指针 + * @return 成功返回0,失败返回错误码 + * - NEU_ERR_GROUP_NOT_EXIST: 组不存在 + * - NEU_ERR_PLUGIN_NOT_SUPPORT_TEMPLATE: + * 插件不支持模板功能(无标签验证器) + * - 其他错误码: 由标签验证器或组添加标签函数返回 + */ int neu_template_add_tag(neu_template_t *tmpl, const char *group, const neu_datatag_t *tag) { - int ret = 0; + int ret = 0; + /* 查找指定的组 */ group_entry_t *ent = NULL; HASH_FIND(hh, tmpl->groups, group, strlen(group), ent); if (NULL == ent) { return NEU_ERR_GROUP_NOT_EXIST; } + /* 检查是否设置了标签验证器 */ if (NULL == tmpl->tag_validator) { return NEU_ERR_PLUGIN_NOT_SUPPORT_TEMPLATE; } + /* 使用验证器验证标签 */ if (0 != (ret = tmpl->tag_validator(tag))) { return ret; } + /* 将标签添加到组中 */ return neu_group_add_tag(ent->group, tag); } +/** + * @brief 更新模板指定组中的标签 + * + * @param tmpl 模板对象指针 + * @param group 组名称 + * @param tag 更新后的标签对象指针 + * @return 成功返回0,失败返回错误码 + * - NEU_ERR_GROUP_NOT_EXIST: 组不存在 + * - NEU_ERR_PLUGIN_NOT_SUPPORT_TEMPLATE: + * 插件不支持模板功能(无标签验证器) + * - 其他错误码: 由标签验证器或组更新标签函数返回 + */ int neu_template_update_tag(neu_template_t *tmpl, const char *group, const neu_datatag_t *tag) { int ret = 0; + /* 查找指定的组 */ group_entry_t *ent = NULL; HASH_FIND(hh, tmpl->groups, group, strlen(group), ent); if (NULL == ent) { return NEU_ERR_GROUP_NOT_EXIST; } + /* 检查是否设置了标签验证器 */ if (NULL == tmpl->tag_validator) { return NEU_ERR_PLUGIN_NOT_SUPPORT_TEMPLATE; } + /* 使用验证器验证标签 */ if (0 != (ret = tmpl->tag_validator(tag))) { return ret; } + /* 更新组中的标签 */ return neu_group_update_tag(ent->group, tag); } diff --git a/src/connection/connection.c b/src/connection/connection.c index 8c12164cf..0bea574c0 100644 --- a/src/connection/connection.c +++ b/src/connection/connection.c @@ -37,61 +37,148 @@ #define CMSPAR 010000000000 /* mark or space (stick) parity */ #endif +/** + * @brief TCP客户端结构体 + * + * 用于存储TCP服务器模式下连接的客户端信息 + */ struct tcp_client { - int fd; - struct sockaddr_in client; + int fd; /**< 客户端套接字描述符 */ + struct sockaddr_in client; /**< 客户端地址信息 */ }; +/** + * @brief Neuron连接结构体 + * + * 管理各种类型连接(TCP客户端/服务器、串口等)的主要结构体 + */ struct neu_conn { - neu_conn_param_t param; - void * data; - bool is_connected; - bool stop; - bool connection_ok; + neu_conn_param_t param; /**< 连接参数 */ + void *data; /**< 用户自定义数据 */ + bool is_connected; /**< 连接状态标志 */ + bool stop; /**< 停止标志 */ + bool connection_ok; /**< 连接是否正常 */ - neu_conn_callback connected; - neu_conn_callback disconnected; - bool callback_trigger; + neu_conn_callback connected; /**< 连接建立回调函数 */ + neu_conn_callback disconnected; /**< 连接断开回调函数 */ + bool callback_trigger; /**< 回调函数是否已触发 */ - pthread_mutex_t mtx; + pthread_mutex_t mtx; /**< 互斥锁,保护连接状态 */ - int fd; - bool block; + int fd; /**< 连接的文件描述符 */ + bool block; /**< 阻塞模式标志 */ - neu_conn_state_t state; + neu_conn_state_t state; /**< 连接状态统计信息 */ + /** + * @brief TCP服务器相关信息 + */ struct { - struct tcp_client *clients; - int n_client; - bool is_listen; + struct tcp_client *clients; /**< 客户端列表 */ + int n_client; /**< 当前连接的客户端数量 */ + bool is_listen; /**< 是否处于监听状态 */ } tcp_server; - uint8_t *buf; - uint16_t buf_size; - uint16_t offset; + uint8_t *buf; /**< 数据缓冲区 */ + uint16_t buf_size; /**< 缓冲区大小 */ + uint16_t offset; /**< 缓冲区偏移量 */ }; +/** + * @brief 向TCP服务器添加客户端 + * + * @param conn 连接对象指针 + * @param fd 客户端套接字描述符 + * @param client 客户端地址信息 + */ static void conn_tcp_server_add_client(neu_conn_t *conn, int fd, struct sockaddr_in client); + +/** + * @brief 从TCP服务器删除客户端 + * + * @param conn 连接对象指针 + * @param fd 客户端套接字描述符 + */ static void conn_tcp_server_del_client(neu_conn_t *conn, int fd); -static int conn_tcp_server_replace_client(neu_conn_t *conn, int fd, - struct sockaddr_in client); +/** + * @brief 替换TCP服务器中的客户端 + * + * 当达到最大连接数时,替换一个客户端 + * + * @param conn 连接对象指针 + * @param fd 新客户端套接字描述符 + * @param client 新客户端地址信息 + * @return 被替换的客户端的套接字描述符,失败返回-1 + */ +static int conn_tcp_server_replace_client(neu_conn_t *conn, int fd, + struct sockaddr_in client); + +/** + * @brief 启动TCP服务器监听 + * + * @param conn 连接对象指针 + */ static void conn_tcp_server_listen(neu_conn_t *conn); + +/** + * @brief 停止TCP服务器监听 + * + * @param conn 连接对象指针 + */ static void conn_tcp_server_stop(neu_conn_t *conn); +/** + * @brief 建立连接 + * + * 根据连接类型执行相应的连接操作 + * + * @param conn 连接对象指针 + */ static void conn_connect(neu_conn_t *conn); + +/** + * @brief 断开连接 + * + * 关闭当前连接 + * + * @param conn 连接对象指针 + */ static void conn_disconnect(neu_conn_t *conn); +/** + * @brief 释放连接参数资源 + * + * @param conn 连接对象指针 + */ static void conn_free_param(neu_conn_t *conn); + +/** + * @brief 初始化连接参数 + * + * @param conn 连接对象指针 + * @param param 连接参数 + */ static void conn_init_param(neu_conn_t *conn, neu_conn_param_t *param); +/** + * @brief 创建新的连接对象 + * + * @param param 连接参数 + * @param data 用户自定义数据,会传递给回调函数 + * @param connected 连接建立时的回调函数 + * @param disconnected 连接断开时的回调函数 + * @return 成功返回连接对象指针,失败返回NULL + */ neu_conn_t *neu_conn_new(neu_conn_param_t *param, void *data, neu_conn_callback connected, neu_conn_callback disconnected) { + /* 分配连接对象内存 */ neu_conn_t *conn = calloc(1, sizeof(neu_conn_t)); + /* 初始化参数 */ conn_init_param(conn, param); conn->is_connected = false; conn->callback_trigger = false; @@ -99,18 +186,28 @@ neu_conn_t *neu_conn_new(neu_conn_param_t *param, void *data, conn->disconnected = disconnected; conn->connected = connected; + /* 初始化缓冲区 */ conn->buf_size = 2048; conn->buf = calloc(conn->buf_size, 1); conn->offset = 0; conn->stop = false; + /* 如果是TCP服务器,启动监听 */ conn_tcp_server_listen(conn); + /* 初始化互斥锁 */ pthread_mutex_init(&conn->mtx, NULL); return conn; } +/** + * @brief 停止连接 + * + * 设置停止标志并断开当前连接 + * + * @param conn 连接对象指针 + */ void neu_conn_stop(neu_conn_t *conn) { pthread_mutex_lock(&conn->mtx); @@ -119,6 +216,13 @@ void neu_conn_stop(neu_conn_t *conn) pthread_mutex_unlock(&conn->mtx); } +/** + * @brief 启动连接 + * + * 清除停止标志,允许连接继续工作 + * + * @param conn 连接对象指针 + */ void neu_conn_start(neu_conn_t *conn) { pthread_mutex_lock(&conn->mtx); @@ -126,17 +230,29 @@ void neu_conn_start(neu_conn_t *conn) pthread_mutex_unlock(&conn->mtx); } +/** + * @brief 重新配置连接 + * + * 断开当前连接,释放旧参数,使用新参数重新初始化连接 + * + * @param conn 连接对象指针 + * @param param 新的连接参数 + * @return 成功返回重新配置后的连接对象指针 + */ neu_conn_t *neu_conn_reconfig(neu_conn_t *conn, neu_conn_param_t *param) { pthread_mutex_lock(&conn->mtx); + /* 断开连接并释放资源 */ conn_disconnect(conn); conn_free_param(conn); conn_tcp_server_stop(conn); + /* 使用新参数初始化 */ conn_init_param(conn, param); conn_tcp_server_listen(conn); + /* 重置状态统计信息 */ conn->state.recv_bytes = 0; conn->state.send_bytes = 0; @@ -145,27 +261,51 @@ neu_conn_t *neu_conn_reconfig(neu_conn_t *conn, neu_conn_param_t *param) return conn; } +/** + * @brief 销毁连接对象 + * + * 停止服务器、断开连接、释放资源并释放连接对象内存 + * + * @param conn 连接对象指针 + */ void neu_conn_destory(neu_conn_t *conn) { pthread_mutex_lock(&conn->mtx); + /* 停止所有活动并释放参数资源 */ conn_tcp_server_stop(conn); conn_disconnect(conn); conn_free_param(conn); pthread_mutex_unlock(&conn->mtx); + /* 销毁互斥锁 */ pthread_mutex_destroy(&conn->mtx); + /* 释放缓冲区和连接对象 */ free(conn->buf); free(conn); } +/** + * @brief 获取连接状态 + * + * @param conn 连接对象指针 + * @return 连接状态结构体 + */ neu_conn_state_t neu_conn_state(neu_conn_t *conn) { return conn->state; } +/** + * @brief TCP服务器接受新的客户端连接 + * + * 接受来自客户端的连接请求,设置超时,并在达到最大连接数时处理 + * + * @param conn 连接对象指针 + * @return 成功返回新客户端的套接字描述符,失败返回-1 + */ int neu_conn_tcp_server_accept(neu_conn_t *conn) { struct sockaddr_in client = { 0 }; @@ -173,11 +313,13 @@ int neu_conn_tcp_server_accept(neu_conn_t *conn) int fd = 0; pthread_mutex_lock(&conn->mtx); + /* 检查连接类型是否为TCP服务器 */ if (conn->param.type != NEU_CONN_TCP_SERVER) { pthread_mutex_unlock(&conn->mtx); return -1; } + /* 接受新的客户端连接 */ fd = accept(conn->fd, (struct sockaddr *) &client, &client_len); if (fd <= 0) { zlog_error(conn->param.log, "%s:%d accpet error: %s", @@ -187,6 +329,7 @@ int neu_conn_tcp_server_accept(neu_conn_t *conn) return -1; } + /* 如果是阻塞模式,设置接收和发送超时 */ if (conn->block) { struct timeval tv = { .tv_sec = conn->param.params.tcp_server.timeout / 1000, @@ -196,12 +339,15 @@ int neu_conn_tcp_server_accept(neu_conn_t *conn) setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)); } + /* 检查是否达到最大连接数 */ if (conn->tcp_server.n_client >= conn->param.params.tcp_server.max_link) { + /* 尝试替换一个已有客户端 */ int free_fd = conn_tcp_server_replace_client(conn, fd, client); if (free_fd > 0) { zlog_warn(conn->param.log, "replace old client %d with %d", free_fd, fd); } else { + /* 无法替换,拒绝连接 */ close(fd); zlog_warn(conn->param.log, "%s:%d accpet max link: %d, reject", conn->param.params.tcp_server.ip, @@ -211,13 +357,16 @@ int neu_conn_tcp_server_accept(neu_conn_t *conn) return -1; } } else { + /* 未达到最大连接数,添加新客户端 */ conn_tcp_server_add_client(conn, fd, client); } + /* 更新连接状态并触发回调函数 */ conn->is_connected = true; conn->connected(conn->data, fd); conn->callback_trigger = true; + /* 记录新连接日志 */ zlog_notice(conn->param.log, "%s:%d accpet new client: %s:%d, fd: %d", conn->param.params.tcp_server.ip, conn->param.params.tcp_server.port, inet_ntoa(client.sin_addr), @@ -228,16 +377,26 @@ int neu_conn_tcp_server_accept(neu_conn_t *conn) return fd; } +/** + * @brief 关闭TCP服务器的客户端连接 + * + * @param conn 连接对象指针 + * @param fd 要关闭的客户端套接字描述符 + * @return 成功返回0,失败返回-1 + */ int neu_conn_tcp_server_close_client(neu_conn_t *conn, int fd) { pthread_mutex_lock(&conn->mtx); + /* 检查连接类型 */ if (conn->param.type != NEU_CONN_TCP_SERVER) { pthread_mutex_unlock(&conn->mtx); return -1; } + /* 触发断开连接回调并删除客户端 */ conn->disconnected(conn->data, fd); conn_tcp_server_del_client(conn, fd); + /* 清空缓冲区 */ conn->offset = 0; memset(conn->buf, 0, conn->buf_size); @@ -245,23 +404,39 @@ int neu_conn_tcp_server_close_client(neu_conn_t *conn, int fd) return 0; } +/** + * @brief TCP服务器向客户端发送数据 + * + * 使用非阻塞方式向指定客户端发送数据 + * + * @param conn 连接对象指针 + * @param fd 客户端套接字描述符 + * @param buf 数据缓冲区 + * @param len 数据长度 + * @return 成功发送的字节数,失败返回-1 + */ ssize_t neu_conn_tcp_server_send(neu_conn_t *conn, int fd, uint8_t *buf, ssize_t len) { ssize_t ret = 0; pthread_mutex_lock(&conn->mtx); + /* 检查是否停止 */ if (conn->stop) { pthread_mutex_unlock(&conn->mtx); return ret; } + /* 确保服务器处于监听状态 */ conn_tcp_server_listen(conn); + /* 以非阻塞方式发送数据,MSG_NOSIGNAL防止管道破裂信号 */ ret = send(fd, buf, len, MSG_NOSIGNAL | MSG_DONTWAIT); if (ret > 0) { + /* 更新发送字节计数 */ conn->state.send_bytes += ret; } + /* 如果发送失败且不是因为缓冲区满(EAGAIN),则断开连接 */ if (ret <= 0 && errno != EAGAIN) { conn->disconnected(conn->data, fd); conn_tcp_server_del_client(conn, fd); @@ -272,27 +447,44 @@ ssize_t neu_conn_tcp_server_send(neu_conn_t *conn, int fd, uint8_t *buf, return ret; } +/** + * @brief TCP服务器从客户端接收数据 + * + * 从指定的客户端套接字接收数据 + * + * @param conn 连接对象指针 + * @param fd 客户端套接字描述符 + * @param buf 数据接收缓冲区 + * @param len 要接收的数据长度 + * @return 成功接收的字节数,失败返回-1或0(连接关闭) + */ ssize_t neu_conn_tcp_server_recv(neu_conn_t *conn, int fd, uint8_t *buf, ssize_t len) { ssize_t ret = 0; pthread_mutex_lock(&conn->mtx); + /* 检查是否停止 */ if (conn->stop) { pthread_mutex_unlock(&conn->mtx); return ret; } + /* 根据阻塞模式选择接收方式 */ if (conn->block) { + /* 阻塞模式等待接收完整数据 */ ret = recv(fd, buf, len, MSG_WAITALL); } else { + /* 非阻塞模式 */ ret = recv(fd, buf, len, 0); } + /* 更新接收字节计数 */ if (ret > 0) { conn->state.recv_bytes += ret; } + /* 如果接收失败或连接关闭,则删除客户端 */ if (ret <= 0) { conn->disconnected(conn->data, fd); conn_tcp_server_del_client(conn, fd); diff --git a/src/connection/mqtt_client.c b/src/connection/mqtt_client.c index f3a06b340..5097d0611 100644 --- a/src/connection/mqtt_client.c +++ b/src/connection/mqtt_client.c @@ -58,31 +58,39 @@ } \ } while (0) +/** + * @brief MQTT订阅信息结构体 + * + * 存储每个MQTT主题订阅的相关信息 + */ typedef struct { - size_t ref; - bool ack; - neu_mqtt_qos_e qos; - char * topic; - neu_mqtt_client_subscribe_cb_t cb; - void * data; - UT_hash_handle hh; + size_t ref; /**< 引用计数 */ + bool ack; /**< 是否已收到订阅确认 */ + neu_mqtt_qos_e qos; /**< 服务质量等级 */ + char *topic; /**< 订阅的主题 */ + neu_mqtt_client_subscribe_cb_t cb; /**< 消息到达回调函数 */ + void *data; /**< 用户自定义数据 */ + UT_hash_handle hh; /**< uthash哈希处理结构 */ } subscription_t; +/** + * @brief MQTT客户端任务类型枚举 + */ typedef enum { - TASK_PUB, - TASK_SUB, - TASK_UNSUB, - TASK_RECV, + TASK_PUB, /**< 发布任务 */ + TASK_SUB, /**< 订阅任务 */ + TASK_UNSUB, /**< 取消订阅任务 */ + TASK_RECV, /**< 接收任务 */ } task_kind_e; #define TASK_UNION_FIELDS \ struct { \ neu_mqtt_client_publish_cb_t cb; \ neu_mqtt_qos_e qos; \ - char * topic; \ - uint8_t * payload; \ + char *topic; \ + uint8_t *payload; \ uint32_t len; \ - void * data; \ + void *data; \ } pub; \ subscription_t *sub; \ struct { \ @@ -93,78 +101,234 @@ typedef union { TASK_UNION_FIELDS; } task_union; +/** + * @brief MQTT客户端任务结构体 + * + * 表示MQTT客户端需要执行的一个操作(发布、订阅、接收等) + */ typedef struct task_s { - task_kind_e kind; - nng_aio * aio; + task_kind_e kind; /**< 任务类型 */ + nng_aio *aio; /**< NNG异步I/O对象 */ union { - TASK_UNION_FIELDS; + TASK_UNION_FIELDS; /**< 根据任务类型不同包含的数据 */ }; - struct task_s *prev; - struct task_s *next; + struct task_s *prev; /**< 双向链表前一个节点 */ + struct task_s *next; /**< 双向链表后一个节点 */ } task_t; +/** + * @brief MQTT客户端结构体 + * + * Neuron MQTT客户端的主要数据结构,管理连接和操作 + */ struct neu_mqtt_client_s { - nng_socket sock; - nng_mtx * mtx; - neu_events_t * events; - neu_event_timer_t * timer; - neu_mqtt_version_e version; - char * host; - uint16_t port; - char * url; - nng_tls_config * tls_cfg; - nng_msg * conn_msg; - nng_duration retry; - bool open; - bool connected; - neu_mqtt_client_connection_cb_t connect_cb; - void * connect_cb_data; - neu_mqtt_client_connection_cb_t disconnect_cb; - void * disconnect_cb_data; - nng_mqtt_sqlite_option * sqlite_cfg; - bool receiving; - nng_aio * recv_aio; - subscription_t * subscriptions; - size_t suback_count; - size_t task_count; - size_t task_limit; - task_t * task_free_list; - zlog_category_t * log; + nng_socket sock; /**< NNG MQTT套接字 */ + nng_mtx *mtx; /**< 互斥锁,保护客户端状态 */ + neu_events_t *events; /**< 事件系统引用 */ + neu_event_timer_t *timer; /**< 重连定时器 */ + neu_mqtt_version_e version; /**< MQTT协议版本 */ + char *host; /**< MQTT服务器主机名/IP */ + uint16_t port; /**< MQTT服务器端口 */ + char *url; /**< 连接URL */ + nng_tls_config *tls_cfg; /**< TLS配置(如果使用) */ + nng_msg *conn_msg; /**< 连接消息 */ + nng_duration retry; /**< 重试间隔(毫秒) */ + bool open; /**< 是否已打开 */ + bool connected; /**< 是否已连接 */ + neu_mqtt_client_connection_cb_t connect_cb; /**< 连接成功回调 */ + void *connect_cb_data; /**< 连接回调用户数据 */ + neu_mqtt_client_connection_cb_t disconnect_cb; /**< 连接断开回调 */ + void *disconnect_cb_data; /**< 断开回调用户数据 */ + nng_mqtt_sqlite_option *sqlite_cfg; /**< SQLite持久化配置 */ + bool receiving; /**< 是否正在接收消息 */ + nng_aio *recv_aio; /**< 接收异步I/O对象 */ + subscription_t *subscriptions; /**< 主题订阅哈希表 */ + size_t suback_count; /**< 订阅确认计数 */ + size_t task_count; /**< 当前任务数量 */ + size_t task_limit; /**< 任务数量限制 */ + task_t *task_free_list; /**< 空闲任务链表 */ + zlog_category_t *log; /**< 日志类别 */ }; +/** + * @brief 创建新的任务对象 + * @param client MQTT客户端对象指针 + * @return 成功返回任务对象指针,失败返回NULL + */ static inline task_t *task_new(neu_mqtt_client_t *client); -static inline void task_free(task_t *task); -static inline void tasks_free(task_t *tasks); -static void task_cb(void *arg); -static void task_handle_pub(task_t *task, neu_mqtt_client_t *client); -static void task_handle_sub(task_t *task, neu_mqtt_client_t *client); + +/** + * @brief 释放任务对象资源 + * @param task 要释放的任务对象指针 + */ +static inline void task_free(task_t *task); + +/** + * @brief 释放任务链表中所有任务 + * @param tasks 任务链表头指针 + */ +static inline void tasks_free(task_t *tasks); + +/** + * @brief NNG异步I/O回调函数 + * @param arg 用户参数(任务对象指针) + */ +static void task_cb(void *arg); + +/** + * @brief 处理发布任务 + * @param task 任务对象指针 + * @param client MQTT客户端对象指针 + */ +static void task_handle_pub(task_t *task, neu_mqtt_client_t *client); + +/** + * @brief 处理订阅任务 + * @param task 任务对象指针 + * @param client MQTT客户端对象指针 + */ +static void task_handle_sub(task_t *task, neu_mqtt_client_t *client); + +/** + * @brief 处理取消订阅任务 + * @param task 任务对象指针 + * @param client MQTT客户端对象指针 + */ static void task_handle_unsub(task_t *task, neu_mqtt_client_t *client); + +/** + * @brief 处理接收任务 + * @param task 任务对象指针 + * @param client MQTT客户端对象指针 + */ static void task_handle_recv(task_t *task, neu_mqtt_client_t *client); -static subscription_t * subscription_new(neu_mqtt_client_t *client, - neu_mqtt_qos_e qos, const char *topic, - neu_mqtt_client_subscribe_cb_t cb, - void * data); -static inline void subscription_free(subscription_t *subscription); +/** + * @brief 创建新的订阅对象 + * @param client MQTT客户端对象指针 + * @param qos 服务质量等级 + * @param topic 订阅的主题 + * @param cb 接收消息回调函数 + * @param data 用户自定义数据 + * @return 成功返回订阅对象指针,失败返回NULL + */ +static subscription_t *subscription_new(neu_mqtt_client_t *client, + neu_mqtt_qos_e qos, const char *topic, + neu_mqtt_client_subscribe_cb_t cb, + void *data); + +/** + * @brief 释放订阅对象资源 + * @param subscription 要释放的订阅对象指针 + */ +static inline void subscription_free(subscription_t *subscription); + +/** + * @brief 增加订阅对象引用计数 + * @param subscription 订阅对象指针 + * @return 返回订阅对象指针 + */ static inline subscription_t *subscription_ref(subscription_t *subscription); -static inline void subscriptions_free(subscription_t *subscriptions); +/** + * @brief 释放订阅哈希表中所有订阅对象 + * @param subscriptions 订阅哈希表头指针 + */ +static inline void subscriptions_free(subscription_t *subscriptions); + +/** + * @brief MQTT消息接收回调函数 + * @param arg 用户参数(MQTT客户端对象指针) + */ static void recv_cb(void *arg); -static int resub_cb(void *data); + +/** + * @brief 重新订阅回调函数 + * @param data 用户数据(MQTT客户端对象指针) + * @return 成功返回0,失败返回错误码 + */ +static int resub_cb(void *data); + +/** + * @brief 连接断开回调函数 + * @param p NNG管道对象 + * @param ev NNG管道事件 + * @param arg 用户参数(MQTT客户端对象指针) + */ static void disconnect_cb(nng_pipe p, nng_pipe_ev ev, void *arg); + +/** + * @brief 连接建立回调函数 + * @param p NNG管道对象 + * @param ev NNG管道事件 + * @param arg 用户参数(MQTT客户端对象指针) + */ static void connect_cb(nng_pipe p, nng_pipe_ev ev, void *arg); +/** + * @brief 从客户端分配一个任务对象 + * @param client MQTT客户端对象指针 + * @return 成功返回任务对象指针,失败返回NULL + */ static inline task_t *client_alloc_task(neu_mqtt_client_t *client); -static inline void client_free_task(neu_mqtt_client_t *client, task_t *task); -static inline size_t client_task_free_list_len(neu_mqtt_client_t *client); -static inline void client_add_subscription(neu_mqtt_client_t *client, - subscription_t * sub); -static int client_send_sub_msg(neu_mqtt_client_t *client, - subscription_t * subscription); -static inline void client_start_recv(neu_mqtt_client_t *client); -static inline int client_start_timer(neu_mqtt_client_t *client); -static inline int client_make_url(neu_mqtt_client_t *client); +/** + * @brief 释放任务对象回客户端的空闲列表 + * @param client MQTT客户端对象指针 + * @param task 要释放的任务对象指针 + */ +static inline void client_free_task(neu_mqtt_client_t *client, task_t *task); + +/** + * @brief 获取客户端空闲任务列表长度 + * @param client MQTT客户端对象指针 + * @return 空闲任务列表长度 + */ +static inline size_t client_task_free_list_len(neu_mqtt_client_t *client); + +/** + * @brief 向客户端添加订阅 + * @param client MQTT客户端对象指针 + * @param sub 订阅对象指针 + */ +static inline void client_add_subscription(neu_mqtt_client_t *client, + subscription_t *sub); + +/** + * @brief 发送订阅消息 + * @param client MQTT客户端对象指针 + * @param subscription 订阅对象指针 + * @return 成功返回0,失败返回错误码 + */ +static int client_send_sub_msg(neu_mqtt_client_t *client, + subscription_t *subscription); + +/** + * @brief 启动客户端接收 + * @param client MQTT客户端对象指针 + */ +static inline void client_start_recv(neu_mqtt_client_t *client); + +/** + * @brief 启动重连定时器 + * @param client MQTT客户端对象指针 + * @return 成功返回0,失败返回错误码 + */ +static inline int client_start_timer(neu_mqtt_client_t *client); + +/** + * @brief 构建连接URL + * @param client MQTT客户端对象指针 + * @return 成功返回0,失败返回错误码 + */ +static inline int client_make_url(neu_mqtt_client_t *client); + +/** + * @brief 将Neuron MQTT版本枚举转换为NNG MQTT版本值 + * + * @param v Neuron MQTT版本枚举 + * @return NNG MQTT版本值 + */ static inline uint8_t neu_mqtt_version_to_nng_mqtt_version(neu_mqtt_version_e v) { switch (v) { @@ -179,14 +343,24 @@ static inline uint8_t neu_mqtt_version_to_nng_mqtt_version(neu_mqtt_version_e v) } } +/** + * @brief 创建新的任务对象 + * + * 分配内存并初始化任务对象的异步I/O + * + * @param client MQTT客户端对象指针 + * @return 成功返回任务对象指针,失败返回NULL + */ static inline task_t *task_new(neu_mqtt_client_t *client) { + /* 分配任务对象内存并初始化为0 */ task_t *task = calloc(1, sizeof(*task)); if (NULL == task) { log(error, "calloc task fail"); return NULL; } + /* 创建NNG异步I/O对象 */ int rv = 0; if ((rv = nng_aio_alloc(&task->aio, task_cb, task)) != 0) { log(error, "nng_aio_alloc fail: %s", nng_strerror(rv)); @@ -194,11 +368,19 @@ static inline task_t *task_new(neu_mqtt_client_t *client) return NULL; } + /* 设置客户端对象作为异步I/O的输入参数 */ nng_aio_set_input(task->aio, 0, client); return task; } +/** + * @brief 释放任务对象资源 + * + * 释放异步I/O对象和任务对象本身的内存 + * + * @param task 要释放的任务对象指针 + */ static inline void task_free(task_t *task) { // NanoSDK quirks: calling nng_aio_stop will block if the aio is in use @@ -207,9 +389,17 @@ static inline void task_free(task_t *task) free(task); } +/** + * @brief 释放任务链表中所有任务 + * + * 遍历链表并释放每个任务对象 + * + * @param tasks 任务链表头指针 + */ static inline void tasks_free(task_t *tasks) { task_t *task = NULL, *tmp = NULL; + /* 安全遍历链表,删除并释放每个节点 */ DL_FOREACH_SAFE(tasks, task, tmp) { DL_DELETE(tasks, task); @@ -217,10 +407,17 @@ static inline void tasks_free(task_t *tasks) } } +/** + * @brief NNG异步I/O回调函数 + * + * 当异步操作完成时被调用,根据任务类型执行相应的处理 + * + * @param arg 用户参数(任务对象指针) + */ static void task_cb(void *arg) { - task_t * task = arg; - nng_aio * aio = task->aio; + task_t *task = arg; + nng_aio *aio = task->aio; neu_mqtt_client_t *client = nng_aio_get_input(aio, 0); if (TASK_PUB == task->kind) { @@ -355,7 +552,7 @@ static void task_handle_recv(task_t *task, neu_mqtt_client_t *client) static subscription_t *subscription_new(neu_mqtt_client_t *client, neu_mqtt_qos_e qos, const char *topic, neu_mqtt_client_subscribe_cb_t cb, - void * data) + void *data) { (void) client; subscription_t *subscription = NULL; @@ -408,7 +605,7 @@ static inline void subscriptions_free(subscription_t *subscriptions) static int resub_cb(void *data) { neu_mqtt_client_t *client = data; - subscription_t * sub = NULL; + subscription_t *sub = NULL; nng_mtx_lock(client->mtx); if (client->connected && @@ -430,9 +627,9 @@ static void connect_cb(nng_pipe p, nng_pipe_ev ev, void *arg) { (void) p; (void) ev; - neu_mqtt_client_t * client = arg; + neu_mqtt_client_t *client = arg; neu_mqtt_client_connection_cb_t cb = NULL; - void * data = NULL; + void *data = NULL; int reason = 0; nng_pipe_get_int(p, NNG_OPT_MQTT_CONNECT_REASON, &reason); @@ -457,10 +654,10 @@ static void disconnect_cb(nng_pipe p, nng_pipe_ev ev, void *arg) { (void) p; (void) ev; - neu_mqtt_client_t * client = arg; - subscription_t * sub = NULL; + neu_mqtt_client_t *client = arg; + subscription_t *sub = NULL; neu_mqtt_client_connection_cb_t cb = NULL; - void * data = NULL; + void *data = NULL; int reason = 0; nng_pipe_get_int(p, NNG_OPT_MQTT_DISCONNECT_REASON, &reason); @@ -470,7 +667,10 @@ static void disconnect_cb(nng_pipe p, nng_pipe_ev ev, void *arg) client->connected = false; cb = client->disconnect_cb; data = client->disconnect_cb_data; - HASH_LOOP(hh, client->subscriptions, sub) { sub->ack = false; } + HASH_LOOP(hh, client->subscriptions, sub) + { + sub->ack = false; + } client->suback_count = 0; nng_mtx_unlock(client->mtx); @@ -482,9 +682,9 @@ static void disconnect_cb(nng_pipe p, nng_pipe_ev ev, void *arg) static void recv_cb(void *arg) { int rv = 0; - subscription_t * subscription = arg; + subscription_t *subscription = arg; neu_mqtt_client_t *client = arg; - nng_aio * aio = client->recv_aio; + nng_aio *aio = client->recv_aio; if (0 != (rv = nng_aio_result(aio))) { log(error, "mqtt client recv error: %s", nng_strerror(rv)); @@ -513,7 +713,7 @@ static void recv_cb(void *arg) } uint32_t payload_len; - uint8_t * payload = nng_mqtt_msg_get_publish_payload(msg, &payload_len); + uint8_t *payload = nng_mqtt_msg_get_publish_payload(msg, &payload_len); uint32_t topic_len; const char *topic = nng_mqtt_msg_get_publish_topic(msg, &topic_len); uint8_t qos = nng_mqtt_msg_get_publish_qos(msg); @@ -605,7 +805,7 @@ static inline size_t client_task_free_list_len(neu_mqtt_client_t *client) } static inline void client_add_subscription(neu_mqtt_client_t *client, - subscription_t * sub) + subscription_t *sub) { subscription_t *old = NULL; @@ -618,7 +818,7 @@ static inline void client_add_subscription(neu_mqtt_client_t *client, } static inline void client_del_subscription(neu_mqtt_client_t *client, - subscription_t * sub) + subscription_t *sub) { HASH_DEL(client->subscriptions, sub); if (sub->ack) { @@ -628,7 +828,7 @@ static inline void client_del_subscription(neu_mqtt_client_t *client, } static int client_send_sub_msg(neu_mqtt_client_t *client, - subscription_t * subscription) + subscription_t *subscription) { int rv = 0; nng_msg *sub_msg = NULL; @@ -665,7 +865,7 @@ static int client_send_sub_msg(neu_mqtt_client_t *client, } static int client_send_unsub_msg(neu_mqtt_client_t *client, - subscription_t * subscription) + subscription_t *subscription) { int rv = 0; nng_msg *sub_msg = NULL; @@ -706,7 +906,7 @@ static inline void client_start_recv(neu_mqtt_client_t *client) static inline int client_start_timer(neu_mqtt_client_t *client) { - neu_events_t * events = NULL; + neu_events_t *events = NULL; neu_event_timer_t *timer = NULL; if (client->events) { @@ -739,7 +939,7 @@ static inline int client_start_timer(neu_mqtt_client_t *client) static inline int client_make_url(neu_mqtt_client_t *client) { - char * url = NULL; + char *url = NULL; const char *fmt = NULL; if (client->tls_cfg) { @@ -799,7 +999,7 @@ static inline nng_mqtt_sqlite_option * alloc_sqlite_config(neu_mqtt_client_t *client) { int rv; - char * db = NULL; + char *db = NULL; nng_mqtt_sqlite_option *cfg = NULL; const mqtt_buf client_id = nng_mqtt_msg_get_connect_client_id(client->conn_msg); @@ -832,18 +1032,27 @@ alloc_sqlite_config(neu_mqtt_client_t *client) return cfg; } +/** + * @brief 创建新的MQTT客户端对象 + * + * @param version MQTT协议版本 + * @return 成功返回MQTT客户端对象指针,失败返回NULL + */ neu_mqtt_client_t *neu_mqtt_client_new(neu_mqtt_version_e version) { + /* 分配客户端对象内存并初始化为0 */ neu_mqtt_client_t *client = calloc(1, sizeof(*client)); if (NULL == client) { return NULL; } + /* 创建互斥锁 */ if (0 != nng_mtx_alloc(&client->mtx)) { free(client); return NULL; } + /* 创建连接消息 */ client->conn_msg = alloc_conn_msg(client, version); if (NULL == client->conn_msg) { nng_mtx_free(client->mtx); @@ -851,6 +1060,7 @@ neu_mqtt_client_t *neu_mqtt_client_new(neu_mqtt_version_e version) return NULL; } + /* 设置客户端参数默认值 */ client->version = version; client->retry = NEU_MQTT_CACHE_SYNC_INTERVAL_DEFAULT; client->task_limit = 1024; @@ -858,14 +1068,24 @@ neu_mqtt_client_t *neu_mqtt_client_new(neu_mqtt_version_e version) return client; } +/** + * @brief 从服务器地址和端口创建MQTT客户端 + * + * @param host 服务器主机名或IP地址 + * @param port 服务器端口 + * @param version MQTT协议版本 + * @return 成功返回MQTT客户端对象指针,失败返回NULL + */ neu_mqtt_client_t *neu_mqtt_client_from_addr(const char *host, uint16_t port, neu_mqtt_version_e version) { + /* 创建新的MQTT客户端 */ neu_mqtt_client_t *client = neu_mqtt_client_new(version); if (NULL == client) { return NULL; } + /* 设置服务器地址 */ if (0 != neu_mqtt_client_set_addr(client, host, port)) { neu_mqtt_client_free(client); return NULL; @@ -874,30 +1094,53 @@ neu_mqtt_client_t *neu_mqtt_client_from_addr(const char *host, uint16_t port, return client; } +/** + * @brief 释放MQTT客户端对象资源 + * + * 释放所有分配的资源,包括TLS配置、SQLite配置、订阅、任务等 + * + * @param client MQTT客户端对象指针 + */ void neu_mqtt_client_free(neu_mqtt_client_t *client) { if (client) { + /* 释放TLS配置 */ if (client->tls_cfg) { nng_tls_config_free(client->tls_cfg); } + /* 释放SQLite持久化配置 */ if (client->sqlite_cfg) { nng_mqtt_free_sqlite_opt(client->sqlite_cfg); } + /* 释放接收异步I/O */ nng_aio_free(client->recv_aio); + /* 释放所有订阅 */ subscriptions_free(client->subscriptions); + /* 释放任务空闲列表 */ tasks_free(client->task_free_list); + /* 释放连接消息 */ nng_msg_free(client->conn_msg); + /* 释放URL和主机名 */ free(client->url); free(client->host); + /* 释放互斥锁 */ nng_mtx_free(client->mtx); + /* 释放客户端对象本身 */ free(client); } } +/** + * @brief 检查MQTT客户端是否已打开 + * + * @param client MQTT客户端对象指针 + * @return 如果客户端已打开返回true,否则返回false + */ bool neu_mqtt_client_is_open(neu_mqtt_client_t *client) { bool open = false; + /* 线程安全地访问客户端状态 */ nng_mtx_lock(client->mtx); open = client->open; nng_mtx_unlock(client->mtx); @@ -986,9 +1229,9 @@ int neu_mqtt_client_set_user(neu_mqtt_client_t *client, const char *username, return 0; } -int neu_mqtt_client_set_connect_cb(neu_mqtt_client_t * client, +int neu_mqtt_client_set_connect_cb(neu_mqtt_client_t *client, neu_mqtt_client_connection_cb_t cb, - void * data) + void *data) { nng_mtx_lock(client->mtx); return_failure_if_open(); @@ -1000,9 +1243,9 @@ int neu_mqtt_client_set_connect_cb(neu_mqtt_client_t * client, return 0; } -int neu_mqtt_client_set_disconnect_cb(neu_mqtt_client_t * client, +int neu_mqtt_client_set_disconnect_cb(neu_mqtt_client_t *client, neu_mqtt_client_connection_cb_t cb, - void * data) + void *data) { nng_mtx_lock(client->mtx); return_failure_if_open(); @@ -1160,7 +1403,7 @@ int neu_mqtt_client_set_cache_sync_interval(neu_mqtt_client_t *client, } int neu_mqtt_client_set_zlog_category(neu_mqtt_client_t *client, - zlog_category_t * cat) + zlog_category_t *cat) { nng_mtx_lock(client->mtx); return_failure_if_open(); @@ -1266,7 +1509,7 @@ int neu_mqtt_client_open(neu_mqtt_client_t *client) int neu_mqtt_client_close(neu_mqtt_client_t *client) { int rv = 0; - neu_events_t * events = NULL; + neu_events_t *events = NULL; neu_event_timer_t *timer = NULL; nng_mtx_lock(client->mtx); @@ -1307,14 +1550,27 @@ int neu_mqtt_client_close(neu_mqtt_client_t *client) return 0; } +/** + * @brief 发布MQTT消息 + * + * @param client MQTT客户端对象指针 + * @param qos 服务质量等级 + * @param topic 发布的主题 + * @param payload 消息负载数据 + * @param len 负载数据长度 + * @param data 用户自定义数据,会传递给回调函数 + * @param cb 发布完成回调函数 + * @return 成功返回0,失败返回-1 + */ int neu_mqtt_client_publish(neu_mqtt_client_t *client, neu_mqtt_qos_e qos, char *topic, uint8_t *payload, uint32_t len, void *data, neu_mqtt_client_publish_cb_t cb) { int rv = 0; nng_msg *pub_msg = NULL; - task_t * task = NULL; + task_t *task = NULL; + /* 分配MQTT消息 */ if (0 != (rv = nng_mqtt_msg_alloc(&pub_msg, 0))) { log(error, "nng_mqtt_msg_alloc fail: %s", nng_strerror(rv)); return -1; diff --git a/src/core/manager.c b/src/core/manager.c index 96103be48..0b16f4553 100644 --- a/src/core/manager.c +++ b/src/core/manager.c @@ -16,6 +16,14 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. **/ + +/** + * @file manager.c + * @brief Neuron系统管理器实现 + * + * 本文件实现了Neuron系统的核心管理器,负责节点、插件、订阅和模板的管理。 + * 管理器提供了一个UDP服务器,用于节点间通信,并处理来自各个节点的请求。 + */ #include #include #include @@ -50,11 +58,30 @@ static int manager_loop(enum neu_event_io_type type, int fd, void *usr_data); inline static void reply(neu_manager_t *manager, neu_reqresp_head_t *header, void *data); -inline static void forward_msg(neu_manager_t * manager, +/** + * @brief 转发消息 + * + * 将消息转发给指定的节点 + * + * @param manager 管理器实例指针 + * @param header 消息头 + * @param node 目标节点名称 + */ +inline static void forward_msg(neu_manager_t *manager, neu_reqresp_head_t *header, const char *node); -inline static void forward_msg_copy(neu_manager_t * manager, + +/** + * @brief 复制并转发消息 + * + * 复制消息并转发给指定的节点 + * + * @param manager 管理器实例指针 + * @param header 消息头 + * @param node 目标节点名称 + */ +inline static void forward_msg_copy(neu_manager_t *manager, neu_reqresp_head_t *header, - const char * node); + const char *node); // inline static nng_msg *trans_data_dup(nng_msg *msg); // inline static void trans_data_free(nng_msg *msg); @@ -62,26 +89,79 @@ inline static void forward_msg_copy(neu_manager_t * manager, // inline static void forward_msg_dup(neu_manager_t *manager, nng_msg *msg, // nng_pipe pipe, msg_dup dup); -inline static void notify_monitors(neu_manager_t * manager, +/** + * @brief 通知所有监控节点 + * + * 将事件通知发送给所有监控节点 + * + * @param manager 管理器实例指针 + * @param event 事件类型 + * @param data 事件数据 + */ +inline static void notify_monitors(neu_manager_t *manager, neu_reqresp_type_e event, void *data); + +/** + * @brief 启动静态适配器 + * + * 启动预定义的静态适配器 + * + * @param manager 管理器实例指针 + * @param name 适配器名称 + */ static void start_static_adapter(neu_manager_t *manager, const char *name); -static int update_timestamp(void *usr_data); + +/** + * @brief 更新时间戳 + * + * 定时更新系统时间戳 + * + * @param usr_data 用户数据,指向管理器实例 + * @return 返回处理结果 + */ +static int update_timestamp(void *usr_data); + +/** + * @brief 启动单例适配器 + * + * 启动只允许单个实例的适配器 + * + * @param manager 管理器实例指针 + * @param name 适配器名称 + * @param plugin_name 插件名称 + * @param display 是否在界面上显示 + */ static void start_single_adapter(neu_manager_t *manager, const char *name, const char *plugin_name, bool display); +/** + * @brief 获取可用端口号 + * + * 生成一个递增的端口号,从10000开始 + * + * @return 返回一个可用的端口号 + */ uint16_t neu_manager_get_port() { static uint16_t port = 10000; return port++; } +/** + * @brief 创建管理器实例 + * + * 创建并初始化Neuron系统管理器,包括事件循环、插件管理器、节点管理器、 + * 订阅管理器和模板管理器等组件,并启动必要的服务。 + * + * @return 返回创建的管理器实例指针 + */ neu_manager_t *neu_manager_create() { int rv = 0; - neu_manager_t * manager = calloc(1, sizeof(neu_manager_t)); + neu_manager_t *manager = calloc(1, sizeof(neu_manager_t)); neu_event_io_param_t param = { - .usr_data = (void *) manager, - .cb = manager_loop, + .usr_data = (void *) manager, + .cb = manager_loop, }; neu_event_timer_param_t timestamp_timer_param = { @@ -152,6 +232,14 @@ neu_manager_t *neu_manager_create() return manager; } +/** + * @brief 销毁管理器实例 + * + * 清理和释放管理器实例占用的所有资源,包括停止所有节点、 + * 释放各个管理器组件和关闭通信套接字。 + * + * @param manager 管理器实例指针 + */ void neu_manager_destroy(neu_manager_t *manager) { neu_req_node_init_t uninit = { 0 }; @@ -193,12 +281,22 @@ void neu_manager_destroy(neu_manager_t *manager) nlog_notice("manager exit"); } +/** + * @brief 管理器主循环回调函数 + * + * 处理UDP通信的事件循环回调函数,接收来自各个节点的消息并进行处理。 + * + * @param type 事件类型 + * @param fd 套接字文件描述符 + * @param usr_data 用户数据,指向管理器实例 + * @return 返回处理结果 + */ static int manager_loop(enum neu_event_io_type type, int fd, void *usr_data) { int rv = 0; - neu_manager_t * manager = (neu_manager_t *) usr_data; + neu_manager_t *manager = (neu_manager_t *) usr_data; struct sockaddr_in src_addr = { 0 }; - neu_msg_t * msg = NULL; + neu_msg_t *msg = NULL; neu_reqresp_head_t *header = NULL; if (type == NEU_EVENT_IO_CLOSED || type == NEU_EVENT_IO_HUP) { @@ -274,7 +372,7 @@ static int manager_loop(enum neu_event_io_type type, int fd, void *usr_data) break; } case NEU_REQ_GET_PLUGIN: { - UT_array * plugins = neu_manager_get_plugins(manager); + UT_array *plugins = neu_manager_get_plugins(manager); neu_resp_get_plugin_t resp = { .plugins = plugins }; header->type = NEU_RESP_GET_PLUGIN; @@ -577,7 +675,7 @@ static int manager_loop(enum neu_event_io_type type, int fd, void *usr_data) case NEU_REQ_DEL_NODE: { neu_req_del_node_t *cmd = (neu_req_del_node_t *) &header[1]; neu_resp_error_t error = { 0 }; - neu_adapter_t * adapter = + neu_adapter_t *adapter = neu_node_manager_find(manager->node_manager, cmd->node); bool single = neu_node_manager_is_single(manager->node_manager, cmd->node); @@ -662,7 +760,7 @@ static int manager_loop(enum neu_event_io_type type, int fd, void *usr_data) } case NEU_REQ_GET_NODE: { neu_req_get_node_t *cmd = (neu_req_get_node_t *) &header[1]; - UT_array * nodes = + UT_array *nodes = neu_manager_get_nodes(manager, cmd->type, cmd->plugin, cmd->node); neu_resp_get_node_t resp = { .nodes = nodes }; @@ -787,7 +885,7 @@ static int manager_loop(enum neu_event_io_type type, int fd, void *usr_data) utarray_foreach(groups, neu_resp_subscribe_info_t *, info) { neu_resp_get_sub_driver_tags_info_t in = { 0 }; - neu_adapter_t * driver = + neu_adapter_t *driver = neu_node_manager_find(manager->node_manager, info->driver); assert(driver != NULL); @@ -1193,7 +1291,7 @@ static int manager_loop(enum neu_event_io_type type, int fd, void *usr_data) return 0; } -inline static void forward_msg(neu_manager_t * manager, +inline static void forward_msg(neu_manager_t *manager, neu_reqresp_head_t *header, const char *node) { struct sockaddr_in addr = @@ -1214,24 +1312,24 @@ inline static void forward_msg(neu_manager_t * manager, } } -inline static void forward_msg_copy(neu_manager_t * manager, +inline static void forward_msg_copy(neu_manager_t *manager, neu_reqresp_head_t *header, - const char * node) + const char *node) { neu_msg_t *msg = neu_msg_copy((neu_msg_t *) header); forward_msg(manager, neu_msg_get_header(msg), node); } struct notify_monitor_ctx { - neu_manager_t * manager; + neu_manager_t *manager; neu_reqresp_type_e event; - void * data; + void *data; }; static int notify_monitor(const char *name, struct sockaddr_in addr, void *arg) { struct notify_monitor_ctx *ctx = arg; - void * data = NULL; + void *data = NULL; neu_req_node_setting_t setting = { 0 }; neu_req_add_tag_t mod_tag = { 0 }; neu_req_del_tag_t del_tag = { 0 }; @@ -1293,7 +1391,7 @@ static int notify_monitor(const char *name, struct sockaddr_in addr, void *arg) return 0; } -inline static void notify_monitors(neu_manager_t * manager, +inline static void notify_monitors(neu_manager_t *manager, neu_reqresp_type_e event, void *data) { struct notify_monitor_ctx ctx = { @@ -1315,12 +1413,20 @@ inline static void notify_monitors(neu_manager_t * manager, } } +/** + * @brief 启动静态适配器 + * + * 加载并初始化静态适配器(内置插件) + * + * @param manager 管理器实例指针 + * @param name 适配器名称 + */ static void start_static_adapter(neu_manager_t *manager, const char *name) { - neu_adapter_t * adapter = NULL; + neu_adapter_t *adapter = NULL; neu_plugin_instance_t instance = { 0 }; neu_adapter_info_t adapter_info = { - .name = name, + .name = name, }; neu_plugin_manager_load_static(manager->plugin_manager, name, &instance); @@ -1333,13 +1439,23 @@ static void start_static_adapter(neu_manager_t *manager, const char *name) neu_adapter_start(adapter); } +/** + * @brief 启动单例适配器 + * + * 加载并初始化单例适配器,每个插件只允许有一个实例 + * + * @param manager 管理器实例指针 + * @param name 适配器名称 + * @param plugin_name 插件名称 + * @param display 是否在界面上显示 + */ static void start_single_adapter(neu_manager_t *manager, const char *name, const char *plugin_name, bool display) { - neu_adapter_t * adapter = NULL; + neu_adapter_t *adapter = NULL; neu_plugin_instance_t instance = { 0 }; neu_adapter_info_t adapter_info = { - .name = name, + .name = name, }; if (0 != @@ -1361,6 +1477,15 @@ static void start_single_adapter(neu_manager_t *manager, const char *name, neu_adapter_start_single(adapter); } +/** + * @brief 回复消息 + * + * 向消息发送者发送回复消息 + * + * @param manager 管理器实例指针 + * @param header 原请求消息头 + * @param data 回复数据 + */ inline static void reply(neu_manager_t *manager, neu_reqresp_head_t *header, void *data) { @@ -1369,7 +1494,7 @@ inline static void reply(neu_manager_t *manager, neu_reqresp_head_t *header, neu_node_manager_get_addr(manager->node_manager, header->receiver); neu_reqresp_type_e t = header->type; - void * ctx = header->ctx; + void *ctx = header->ctx; char receiver[NEU_NODE_NAME_LEN] = { 0 }; strncpy(receiver, header->receiver, sizeof(receiver)); @@ -1385,6 +1510,14 @@ inline static void reply(neu_manager_t *manager, neu_reqresp_head_t *header, } } +/** + * @brief 更新全局时间戳 + * + * 定时更新系统全局时间戳 + * + * @param usr_data 用户数据(未使用) + * @return 始终返回0 + */ static int update_timestamp(void *usr_data) { (void) usr_data; diff --git a/src/core/manager_internal.c b/src/core/manager_internal.c index 28da24fc6..4b1c4bf79 100644 --- a/src/core/manager_internal.c +++ b/src/core/manager_internal.c @@ -31,47 +31,87 @@ #include "manager_internal.h" #include "template_manager.h" +/** + * @brief 添加插件库 + * + * 向管理器中添加一个新的插件库 + * + * @param manager 管理器对象指针 + * @param library 插件库文件路径 + * @return 成功返回0,失败返回错误码 + */ int neu_manager_add_plugin(neu_manager_t *manager, const char *library) { return neu_plugin_manager_add(manager->plugin_manager, library); } +/** + * @brief 删除插件 + * + * 从管理器中删除指定的插件 + * + * @param manager 管理器对象指针 + * @param plugin 插件名称 + * @return 成功返回0,失败返回错误码 + */ int neu_manager_del_plugin(neu_manager_t *manager, const char *plugin) { return neu_plugin_manager_del(manager->plugin_manager, plugin); } +/** + * @brief 获取所有插件列表 + * + * @param manager 管理器对象指针 + * @return 插件列表数组 + */ UT_array *neu_manager_get_plugins(neu_manager_t *manager) { return neu_plugin_manager_get(manager->plugin_manager); } +/** + * @brief 添加节点 + * + * 基于指定的插件创建并添加一个新节点到管理器 + * + * @param manager 管理器对象指针 + * @param node_name 节点名称 + * @param plugin_name 插件名称 + * @param state 节点初始运行状态 + * @param load 是否从持久化存储加载配置 + * @return 成功返回NEU_ERR_SUCCESS,失败返回错误码 + */ int neu_manager_add_node(neu_manager_t *manager, const char *node_name, - const char * plugin_name, + const char *plugin_name, neu_node_running_state_e state, bool load) { - neu_adapter_t * adapter = NULL; + neu_adapter_t *adapter = NULL; neu_plugin_instance_t instance = { 0 }; neu_adapter_info_t adapter_info = { - .name = node_name, + .name = node_name, }; neu_resp_plugin_info_t info = { 0 }; - int ret = + /* 查找指定的插件 */ + int ret = neu_plugin_manager_find(manager->plugin_manager, plugin_name, &info); if (ret != 0) { return NEU_ERR_LIBRARY_NOT_FOUND; } + /* 检查插件是否为单例类型 */ if (info.single) { return NEU_ERR_LIBRARY_NOT_ALLOW_CREATE_INSTANCE; } + /* 检查节点名称是否已存在 */ adapter = neu_node_manager_find(manager->node_manager, node_name); if (adapter != NULL) { return NEU_ERR_NODE_EXIST; } + /* 创建插件实例 */ ret = neu_plugin_manager_create_instance(manager->plugin_manager, info.name, &instance); if (ret != 0) { @@ -80,18 +120,31 @@ int neu_manager_add_node(neu_manager_t *manager, const char *node_name, adapter_info.handle = instance.handle; adapter_info.module = instance.module; + /* 创建适配器 */ adapter = neu_adapter_create(&adapter_info, load); if (adapter == NULL) { return neu_adapter_error(); } + /* 添加节点到管理器 */ neu_node_manager_add(manager->node_manager, adapter); + /* 初始化适配器 */ neu_adapter_init(adapter, state); return NEU_ERR_SUCCESS; } +/** + * @brief 删除节点 + * + * 删除管理器中指定的节点 + * + * @param manager 管理器对象指针 + * @param node_name 要删除的节点名称 + * @return 成功返回NEU_ERR_SUCCESS,失败返回错误码 + */ int neu_manager_del_node(neu_manager_t *manager, const char *node_name) { + /* 查找节点 */ neu_adapter_t *adapter = neu_node_manager_find(manager->node_manager, node_name); @@ -99,22 +152,47 @@ int neu_manager_del_node(neu_manager_t *manager, const char *node_name) return NEU_ERR_NODE_NOT_EXIST; } + /* 销毁适配器 */ neu_adapter_destroy(adapter); + /* 移除相关的订阅 */ neu_subscribe_manager_remove(manager->subscribe_manager, node_name, NULL); + /* 从节点管理器中删除 */ neu_node_manager_del(manager->node_manager, node_name); return NEU_ERR_SUCCESS; } +/** + * @brief 获取节点列表 + * + * 根据过滤条件获取符合条件的节点列表 + * + * @param manager 管理器对象指针 + * @param type 节点类型过滤条件 + * @param plugin 插件名称过滤条件(可为NULL) + * @param node 节点名称过滤条件(可为NULL) + * @return 符合条件的节点列表数组 + */ UT_array *neu_manager_get_nodes(neu_manager_t *manager, int type, const char *plugin, const char *node) { return neu_node_manager_filter(manager->node_manager, type, plugin, node); } +/** + * @brief 更新节点名称 + * + * 修改指定节点的名称,同时更新所有相关的订阅 + * + * @param manager 管理器对象指针 + * @param node 当前节点名称 + * @param new_name 新的节点名称 + * @return 成功返回0,失败返回错误码 + */ int neu_manager_update_node_name(neu_manager_t *manager, const char *node, const char *new_name) { int ret = 0; + /* 根据节点类型(驱动或应用)更新订阅管理器中的名称 */ if (neu_node_manager_is_driver(manager->node_manager, node)) { ret = neu_subscribe_manager_update_driver_name( manager->subscribe_manager, node, new_name); @@ -122,6 +200,7 @@ int neu_manager_update_node_name(neu_manager_t *manager, const char *node, ret = neu_subscribe_manager_update_app_name(manager->subscribe_manager, node, new_name); } + /* 更新节点管理器中的名称 */ if (0 == ret) { ret = neu_node_manager_update_name(manager->node_manager, node, new_name); @@ -129,6 +208,17 @@ int neu_manager_update_node_name(neu_manager_t *manager, const char *node, return ret; } +/** + * @brief 更新组名称 + * + * 修改驱动节点中指定组的名称 + * + * @param manager 管理器对象指针 + * @param driver 驱动节点名称 + * @param group 当前组名称 + * @param new_name 新的组名称 + * @return 成功返回0,失败返回错误码 + */ int neu_manager_update_group_name(neu_manager_t *manager, const char *driver, const char *group, const char *new_name) { @@ -136,14 +226,25 @@ int neu_manager_update_group_name(neu_manager_t *manager, const char *driver, driver, group, new_name); } +/** + * @brief 创建新的插件实例 + * + * 分配内存并创建指定插件的实例 + * + * @param plugin_manager 插件管理器指针 + * @param plugin 插件名称 + * @return 成功返回插件实例指针,失败返回NULL + */ static inline neu_plugin_instance_t * new_plugin_instance(neu_plugin_manager_t *plugin_manager, const char *plugin) { + /* 分配插件实例内存 */ neu_plugin_instance_t *inst = calloc(1, sizeof(*inst)); if (NULL == inst) { return NULL; } + /* 创建插件实例 */ if (0 != neu_plugin_manager_create_instance(plugin_manager, plugin, inst)) { free(inst); return NULL; @@ -152,34 +253,55 @@ new_plugin_instance(neu_plugin_manager_t *plugin_manager, const char *plugin) return inst; } +/** + * @brief 释放插件实例资源 + * + * @param inst 要释放的插件实例指针 + */ static inline void free_plugin_instance(neu_plugin_instance_t *inst) { if (inst) { - dlclose(inst->handle); + dlclose(inst->handle); /* 关闭动态库句柄 */ free(inst); } } +/** + * @brief 添加模板 + * + * 创建并添加一个新的设备模板 + * + * @param manager 管理器对象指针 + * @param name 模板名称 + * @param plugin 使用的插件名称 + * @param n_group 组数量 + * @param groups 组信息数组 + * @return 成功返回0,失败返回错误码 + */ int neu_manager_add_template(neu_manager_t *manager, const char *name, const char *plugin, uint16_t n_group, neu_reqresp_template_group_t *groups) { int rv = 0; + /* 检查插件是否存在 */ if (!neu_plugin_manager_exists(manager->plugin_manager, plugin)) { return NEU_ERR_PLUGIN_NOT_FOUND; } + /* 检查插件是否为单例类型 */ if (neu_plugin_manager_is_single(manager->plugin_manager, plugin)) { return NEU_ERR_PLUGIN_NOT_SUPPORT_TEMPLATE; } + /* 创建插件实例以获取其功能 */ neu_plugin_instance_t *plug_inst = new_plugin_instance(manager->plugin_manager, plugin); if (NULL == plug_inst) { return NEU_ERR_EINTERNAL; } + /* 检查插件是否为驱动类型 */ if (NEU_NA_TYPE_DRIVER != plug_inst->module->type) { free_plugin_instance(plug_inst); return NEU_ERR_PLUGIN_NOT_SUPPORT_TEMPLATE; @@ -218,28 +340,53 @@ int neu_manager_add_template(neu_manager_t *manager, const char *name, return rv; } +/** + * @brief 删除模板 + * + * 根据名称删除已存在的设备模板 + * + * @param manager 管理器对象指针 + * @param name 要删除的模板名称 + * @return 成功返回0,失败返回错误码 + */ int neu_manager_del_template(neu_manager_t *manager, const char *name) { return neu_template_manager_del(manager->template_manager, name); } +/** + * @brief 清除所有模板 + * + * 删除管理器中的所有模板 + * + * @param manager 管理器对象指针 + */ void neu_manager_clear_template(neu_manager_t *manager) { return neu_template_manager_clear(manager->template_manager); } +/** + * @brief 累积模板组信息 + * + * 收集模板组信息并填充到响应结构中,作为回调函数使用 + * + * @param grp 组对象指针 + * @param data 用户数据指针,指向响应结构 + * @return 始终返回0表示成功 + */ static int accumulate_template_group(neu_group_t *grp, void *data) { neu_reqresp_template_t *resp = data; - // increase group count + // 增加组计数 neu_reqresp_template_group_t *resp_grp = &resp->groups[resp->n_group++]; - // copy group name and interval + // 复制组名称和间隔 strcpy(resp_grp->name, neu_group_get_name(grp)); resp_grp->interval = neu_group_get_interval(grp); - // copy tags + // 复制标签 UT_array *tags = neu_group_get_tag(grp); resp_grp->n_tag = utarray_len(tags); resp_grp->tags = utarray_steal(tags); @@ -248,6 +395,16 @@ static int accumulate_template_group(neu_group_t *grp, void *data) return 0; } +/** + * @brief 获取模板信息 + * + * 根据模板名称获取完整的模板详细信息,包括所有组和标签 + * + * @param manager 管理器对象指针 + * @param name 模板名称 + * @param resp 用于返回模板信息的响应结构指针 + * @return 成功返回0,失败返回错误码 + */ int neu_manager_get_template(neu_manager_t *manager, const char *name, neu_resp_get_template_t *resp) { @@ -283,6 +440,15 @@ int neu_manager_get_template(neu_manager_t *manager, const char *name, return 0; } +/** + * @brief 累积模板基本信息 + * + * 收集模板基本信息并填充到响应结构中,作为回调函数使用 + * + * @param tmpl 模板对象指针 + * @param data 用户数据指针,指向响应结构 + * @return 始终返回0表示成功 + */ static int accumulate_template(neu_template_t *tmpl, void *data) { neu_resp_get_templates_t *resp = data; @@ -292,7 +458,16 @@ static int accumulate_template(neu_template_t *tmpl, void *data) return 0; } -int neu_manager_get_templates(neu_manager_t * manager, +/** + * @brief 获取所有模板列表 + * + * 获取系统中所有模板的基本信息列表 + * + * @param manager 管理器对象指针 + * @param resp 用于返回模板列表的响应结构指针 + * @return 成功返回0,失败返回错误码 + */ +int neu_manager_get_templates(neu_manager_t *manager, neu_resp_get_templates_t *resp) { int count = neu_template_manager_count(manager->template_manager); @@ -306,12 +481,23 @@ int neu_manager_get_templates(neu_manager_t * manager, return NEU_ERR_EINTERNAL; } - // guaranteed to success + // 保证成功执行 neu_template_manager_for_each(manager->template_manager, accumulate_template, resp); return 0; } +/** + * @brief 添加模板组 + * + * 向指定模板添加新的组 + * + * @param manager 管理器对象指针 + * @param tmpl_name 模板名称 + * @param group 要添加的组名称 + * @param interval 组的采集间隔 + * @return 成功返回0,失败返回错误码 + */ int neu_manager_add_template_group(neu_manager_t *manager, const char *tmpl_name, const char *group, uint32_t interval) @@ -325,7 +511,16 @@ int neu_manager_add_template_group(neu_manager_t *manager, return neu_template_add_group(tmpl, group, interval); } -int neu_manager_update_template_group(neu_manager_t * manager, +/** + * @brief 更新模板组信息 + * + * 更新模板中指定组的名称和采集间隔 + * + * @param manager 管理器对象指针 + * @param req 包含更新信息的请求结构指针 + * @return 成功返回0,失败返回错误码 + */ +int neu_manager_update_template_group(neu_manager_t *manager, neu_req_update_template_group_t *req) { neu_template_t *tmpl = @@ -336,6 +531,7 @@ int neu_manager_update_template_group(neu_manager_t * manager, int rv = 0; + // 如果提供了新名称且不同于当前名称,则更新组名称 if (strlen(req->new_name) > 0 && 0 != strcmp(req->group, req->new_name)) { rv = neu_template_update_group_name(tmpl, req->group, req->new_name); if (0 != rv) { @@ -343,6 +539,7 @@ int neu_manager_update_template_group(neu_manager_t * manager, } } + // 如果间隔值有效,则更新组的采集间隔 if (req->interval >= NEU_GROUP_INTERVAL_LIMIT) { rv = neu_template_update_group_interval(tmpl, req->new_name, req->interval); @@ -351,7 +548,16 @@ int neu_manager_update_template_group(neu_manager_t * manager, return rv; } -int neu_manager_del_template_group(neu_manager_t * manager, +/** + * @brief 删除模板组 + * + * 从指定模板中删除一个组 + * + * @param manager 管理器对象指针 + * @param req 包含删除信息的请求结构指针 + * @return 成功返回0,失败返回错误码 + */ +int neu_manager_del_template_group(neu_manager_t *manager, neu_req_del_template_group_t *req) { neu_template_t *tmpl = @@ -363,11 +569,21 @@ int neu_manager_del_template_group(neu_manager_t * manager, return neu_template_del_group(tmpl, req->group); } +/** + * @brief 累积组信息 + * + * 收集组信息并填充到数组中,作为回调函数使用 + * + * @param grp 组对象指针 + * @param data 用户数据指针,指向UT_array指针 + * @return 始终返回0表示成功 + */ static int accumulate_group_info(neu_group_t *grp, void *data) { - UT_array ** vec = data; + UT_array **vec = data; neu_resp_group_info_t info = { 0 }; + // 收集组信息 info.interval = neu_group_get_interval(grp); info.tag_count = neu_group_tag_size(grp); strncpy(info.name, neu_group_get_name(grp), sizeof(info.name)); @@ -376,9 +592,19 @@ static int accumulate_group_info(neu_group_t *grp, void *data) return 0; } -int neu_manager_get_template_group(neu_manager_t * manager, +/** + * @brief 获取模板组信息 + * + * 获取指定模板的所有组信息 + * + * @param manager 管理器对象指针 + * @param req 包含请求信息的结构指针 + * @param group_info_p 用于返回组信息数组的指针 + * @return 成功返回0,失败返回错误码 + */ +int neu_manager_get_template_group(neu_manager_t *manager, neu_req_get_template_group_t *req, - UT_array ** group_info_p) + UT_array **group_info_p) { neu_template_t *tmpl = neu_template_manager_find(manager->template_manager, req->tmpl); @@ -404,6 +630,19 @@ int neu_manager_get_template_group(neu_manager_t * manager, return ret; } +/** + * @brief 添加模板标签 + * + * 向指定模板的组中添加多个标签 + * + * @param manager 管理器对象指针 + * @param tmpl_name 模板名称 + * @param group 组名称 + * @param n_tag 标签数量 + * @param tags 标签数组 + * @param index_p 用于返回成功添加的标签数量的指针 + * @return 成功返回0,失败返回错误码 + */ int neu_manager_add_template_tags(neu_manager_t *manager, const char *tmpl_name, const char *group, uint16_t n_tag, neu_datatag_t *tags, uint16_t *index_p) @@ -423,6 +662,7 @@ int neu_manager_add_template_tags(neu_manager_t *manager, const char *tmpl_name, } } + // 如果提供了index_p指针,返回成功添加的标签数量 if (index_p) { *index_p = i; } @@ -430,9 +670,19 @@ int neu_manager_add_template_tags(neu_manager_t *manager, const char *tmpl_name, return ret; } -int neu_manager_update_template_tags(neu_manager_t * manager, +/** + * @brief 更新模板标签 + * + * 更新指定模板组中的多个标签 + * + * @param manager 管理器对象指针 + * @param req 包含更新信息的请求结构指针 + * @param index_p 用于返回成功更新的标签数量的指针 + * @return 成功返回0,失败返回错误码 + */ +int neu_manager_update_template_tags(neu_manager_t *manager, neu_req_update_template_tag_t *req, - uint16_t * index_p) + uint16_t *index_p) { int ret = 0; @@ -450,6 +700,7 @@ int neu_manager_update_template_tags(neu_manager_t * manager, } } + // 如果提供了index_p指针,返回成功更新的标签数量 if (index_p) { *index_p = i; } @@ -457,7 +708,16 @@ int neu_manager_update_template_tags(neu_manager_t * manager, return ret; } -int neu_manager_del_template_tags(neu_manager_t * manager, +/** + * @brief 删除模板标签 + * + * 从指定模板组中删除多个标签 + * + * @param manager 管理器对象指针 + * @param req 包含删除信息的请求结构指针 + * @return 成功返回0,失败返回错误码 + */ +int neu_manager_del_template_tags(neu_manager_t *manager, neu_req_del_template_tag_t *req) { int ret = 0; @@ -471,17 +731,27 @@ int neu_manager_del_template_tags(neu_manager_t * manager, int i = 0; for (; i < req->n_tag; ++i) { - // the only possible error is that the tag does not exist - // in which case we just ignore and continue + // 唯一可能的错误是标签不存在 + // 此时我们忽略错误并继续处理 neu_group_del_tag(grp, req->tags[i]); } return 0; } -int neu_manager_get_template_tags(neu_manager_t * manager, +/** + * @brief 获取模板标签 + * + * 获取指定模板组中的标签,可以根据名称过滤 + * + * @param manager 管理器对象指针 + * @param req 包含请求信息的结构指针 + * @param tags_p 用于返回标签数组的指针 + * @return 成功返回0,失败返回错误码 + */ +int neu_manager_get_template_tags(neu_manager_t *manager, neu_req_get_template_tag_t *req, - UT_array ** tags_p) + UT_array **tags_p) { int ret = 0; neu_group_t *grp = NULL; @@ -492,6 +762,8 @@ int neu_manager_get_template_tags(neu_manager_t * manager, return ret; } + // 如果提供了标签名称,则根据名称查询特定标签 + // 否则获取组中的所有标签 if (strlen(req->name) > 0) { *tags_p = neu_group_query_tag(grp, req->name); } else { @@ -504,7 +776,7 @@ int neu_manager_get_template_tags(neu_manager_t * manager, static int add_template_group(neu_group_t *grp, void *data) { neu_adapter_driver_t *driver = data; - const char * name = neu_group_get_name(grp); + const char *name = neu_group_get_name(grp); uint32_t interval = neu_group_get_interval(grp); if (interval < NEU_GROUP_INTERVAL_LIMIT) { @@ -551,8 +823,8 @@ static int add_template_group(neu_group_t *grp, void *data) } int neu_manager_instantiate_template(neu_manager_t *manager, - const char * tmpl_name, - const char * node_name) + const char *tmpl_name, + const char *node_name) { int ret = 0; @@ -598,7 +870,7 @@ int neu_manager_instantiate_template(neu_manager_t *manager, return ret; } -int neu_manager_instantiate_templates(neu_manager_t * manager, +int neu_manager_instantiate_templates(neu_manager_t *manager, neu_req_inst_templates_t *req) { int ret = 0; @@ -664,7 +936,7 @@ static inline int manager_subscribe(neu_manager_t *manager, const char *app, { int ret = NEU_ERR_SUCCESS; struct sockaddr_in addr = { 0 }; - neu_adapter_t * adapter = + neu_adapter_t *adapter = neu_node_manager_find(manager->node_manager, driver); if (adapter == NULL) { @@ -801,7 +1073,7 @@ UT_array *neu_manager_get_sub_group(neu_manager_t *manager, const char *app) } UT_array *neu_manager_get_sub_group_deep_copy(neu_manager_t *manager, - const char * app) + const char *app) { UT_array *subs = neu_subscribe_manager_get(manager->subscribe_manager, app); diff --git a/src/core/node_manager.c b/src/core/node_manager.c index a96c02090..a374b2e2d 100644 --- a/src/core/node_manager.c +++ b/src/core/node_manager.c @@ -17,6 +17,14 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. **/ +/** + * @file node_manager.c + * @brief 节点管理器实现 + * + * 本文件实现了Neuron系统的节点管理器,负责管理系统中的所有节点(驱动和应用)。 + * 节点管理器提供节点的注册、查找、更新和删除等功能,同时维护节点的网络地址信息。 + */ + #include #include @@ -25,24 +33,41 @@ #include "adapter/adapter_internal.h" #include "node_manager.h" +/** + * @brief 节点实体结构 + * + * 表示系统中的一个节点实体,包含节点名称、适配器指针和网络地址等信息 + */ typedef struct node_entity { - char *name; + char *name; ///< 节点名称 - neu_adapter_t * adapter; - bool is_static; - bool display; - bool single; - bool is_monitor; - struct sockaddr_in addr; + neu_adapter_t *adapter; ///< 节点对应的适配器 + bool is_static; ///< 是否为静态节点 + bool display; ///< 是否在界面上显示 + bool single; ///< 是否为单例节点 + bool is_monitor; ///< 是否为监控节点 + struct sockaddr_in addr; ///< 节点的网络地址 - UT_hash_handle hh; + UT_hash_handle hh; ///< 哈希表句柄,用于uthash } node_entity_t; +/** + * @brief 节点管理器结构 + * + * 管理系统中的所有节点实体 + */ struct neu_node_manager { - node_entity_t *nodes; - UT_array * monitors; + node_entity_t *nodes; ///< 节点哈希表 + UT_array *monitors; ///< 监控节点数组 }; +/** + * @brief 创建节点管理器 + * + * 分配并初始化节点管理器对象 + * + * @return 返回新创建的节点管理器对象,失败则返回NULL + */ neu_node_manager_t *neu_node_manager_create() { neu_node_manager_t *node_manager = calloc(1, sizeof(neu_node_manager_t)); @@ -54,10 +79,18 @@ neu_node_manager_t *neu_node_manager_create() return node_manager; } +/** + * @brief 销毁节点管理器 + * + * 释放节点管理器及其管理的所有节点资源 + * + * @param mgr 节点管理器对象 + */ void neu_node_manager_destroy(neu_node_manager_t *mgr) { node_entity_t *el = NULL, *tmp = NULL; + // 遍历并释放所有节点实体 HASH_ITER(hh, mgr->nodes, el, tmp) { HASH_DEL(mgr->nodes, el); @@ -69,6 +102,15 @@ void neu_node_manager_destroy(neu_node_manager_t *mgr) free(mgr); } +/** + * @brief 添加节点到管理器 + * + * 将适配器对应的节点添加到节点管理器中 + * + * @param mgr 节点管理器对象 + * @param adapter 适配器对象 + * @return 成功返回0,失败返回错误码 + */ int neu_node_manager_add(neu_node_manager_t *mgr, neu_adapter_t *adapter) { node_entity_t *node = calloc(1, sizeof(node_entity_t)); @@ -76,11 +118,14 @@ int neu_node_manager_add(neu_node_manager_t *mgr, neu_adapter_t *adapter) node->adapter = adapter; node->name = strdup(adapter->name); node->display = true; + // 检查是否为监控节点 node->is_monitor = (0 == strcmp(node->adapter->module->module_name, "Monitor")); + // 添加到哈希表 HASH_ADD_STR(mgr->nodes, name, node); + // 如果是监控节点,添加到监控节点列表 if (node->is_monitor) { utarray_push_back(mgr->monitors, &node); } @@ -88,6 +133,15 @@ int neu_node_manager_add(neu_node_manager_t *mgr, neu_adapter_t *adapter) return 0; } +/** + * @brief 添加静态节点到管理器 + * + * 将静态适配器对应的节点添加到节点管理器中。静态节点通常是系统内置的节点。 + * + * @param mgr 节点管理器对象 + * @param adapter 适配器对象 + * @return 成功返回0,失败返回错误码 + */ int neu_node_manager_add_static(neu_node_manager_t *mgr, neu_adapter_t *adapter) { node_entity_t *node = calloc(1, sizeof(node_entity_t)); @@ -102,6 +156,16 @@ int neu_node_manager_add_static(neu_node_manager_t *mgr, neu_adapter_t *adapter) return 0; } +/** + * @brief 添加单例节点到管理器 + * + * 将单例适配器对应的节点添加到节点管理器中。单例节点是特定插件只能有一个实例的节点。 + * + * @param mgr 节点管理器对象 + * @param adapter 适配器对象 + * @param display 是否在界面上显示该节点 + * @return 成功返回0,失败返回错误码 + */ int neu_node_manager_add_single(neu_node_manager_t *mgr, neu_adapter_t *adapter, bool display) { @@ -117,11 +181,22 @@ int neu_node_manager_add_single(neu_node_manager_t *mgr, neu_adapter_t *adapter, return 0; } +/** + * @brief 更新节点名称 + * + * 修改节点的名称,并更新哈希表索引 + * + * @param mgr 节点管理器对象 + * @param node_name 当前节点名称 + * @param new_node_name 新的节点名称 + * @return 成功返回0,失败返回错误码 + */ int neu_node_manager_update_name(neu_node_manager_t *mgr, const char *node_name, const char *new_node_name) { node_entity_t *node = NULL; + // 查找节点 HASH_FIND_STR(mgr->nodes, node_name, node); if (NULL == node) { return NEU_ERR_NODE_NOT_EXIST; @@ -132,6 +207,7 @@ int neu_node_manager_update_name(neu_node_manager_t *mgr, const char *node_name, return NEU_ERR_EINTERNAL; } + // 从哈希表删除,更新名称后重新添加 HASH_DEL(mgr->nodes, node); free(node->name); node->name = new_name; @@ -140,20 +216,40 @@ int neu_node_manager_update_name(neu_node_manager_t *mgr, const char *node_name, return 0; } +/** + * @brief 更新节点网络地址 + * + * 更新指定节点的网络地址信息 + * + * @param mgr 节点管理器对象 + * @param name 节点名称 + * @param addr 新的网络地址 + * @return 成功返回0,失败返回-1 + */ int neu_node_manager_update(neu_node_manager_t *mgr, const char *name, struct sockaddr_in addr) { node_entity_t *node = NULL; + // 查找节点 HASH_FIND_STR(mgr->nodes, name, node); if (NULL == node) { return -1; } + // 更新地址 node->addr = addr; return 0; } +/** + * @brief 删除节点 + * + * 从节点管理器中删除指定的节点 + * + * @param mgr 节点管理器对象 + * @param name 要删除的节点名称 + */ void neu_node_manager_del(neu_node_manager_t *mgr, const char *name) { node_entity_t *node = NULL; @@ -161,6 +257,7 @@ void neu_node_manager_del(neu_node_manager_t *mgr, const char *name) HASH_FIND_STR(mgr->nodes, name, node); if (node != NULL) { HASH_DEL(mgr->nodes, node); + // 如果是监控节点,从监控节点列表中移除 if (node->is_monitor) { utarray_erase(mgr->monitors, utarray_eltidx(mgr->monitors, node), 1); @@ -170,17 +267,34 @@ void neu_node_manager_del(neu_node_manager_t *mgr, const char *name) } } +/** + * @brief 获取节点数量 + * + * 返回节点管理器中的节点总数 + * + * @param mgr 节点管理器对象 + * @return 节点数量 + */ uint16_t neu_node_manager_size(neu_node_manager_t *mgr) { return HASH_COUNT(mgr->nodes); } +/** + * @brief 检查是否存在未初始化节点 + * + * 检查节点管理器中是否存在未初始化的节点(没有网络地址的节点) + * + * @param mgr 节点管理器对象 + * @return 如果存在未初始化节点返回true,否则返回false + */ bool neu_node_manager_exist_uninit(neu_node_manager_t *mgr) { node_entity_t *el = NULL, *tmp = NULL; HASH_ITER(hh, mgr->nodes, el, tmp) { + // 端口为0表示节点未初始化 if (el->addr.sin_port == 0) { return true; } @@ -189,9 +303,18 @@ bool neu_node_manager_exist_uninit(neu_node_manager_t *mgr) return false; } +/** + * @brief 获取特定类型的节点信息 + * + * 根据类型获取节点信息列表,不包括静态节点和不显示的节点 + * + * @param mgr 节点管理器对象 + * @param type 节点类型过滤条件 + * @return 节点信息数组 + */ UT_array *neu_node_manager_get(neu_node_manager_t *mgr, int type) { - UT_array * array = NULL; + UT_array *array = NULL; UT_icd icd = { sizeof(neu_resp_node_info_t), NULL, NULL, NULL }; node_entity_t *el = NULL, *tmp = NULL; @@ -199,7 +322,9 @@ UT_array *neu_node_manager_get(neu_node_manager_t *mgr, int type) HASH_ITER(hh, mgr->nodes, el, tmp) { + // 排除静态节点和不显示的节点 if (!el->is_static && el->display) { + // 按类型过滤 if (el->adapter->module->type & type) { neu_resp_node_info_t info = { 0 }; strcpy(info.node, el->adapter->name); @@ -212,10 +337,21 @@ UT_array *neu_node_manager_get(neu_node_manager_t *mgr, int type) return array; } +/** + * @brief 过滤获取节点信息 + * + * 根据类型、插件名称和节点名称过滤获取节点信息列表 + * + * @param mgr 节点管理器对象 + * @param type 节点类型过滤条件 + * @param plugin 插件名称过滤条件,空字符串表示不过滤 + * @param node 节点名称过滤条件,空字符串表示不过滤 + * @return 过滤后的节点信息数组 + */ UT_array *neu_node_manager_filter(neu_node_manager_t *mgr, int type, const char *plugin, const char *node) { - UT_array * array = NULL; + UT_array *array = NULL; UT_icd icd = { sizeof(neu_resp_node_info_t), NULL, NULL, NULL }; node_entity_t *el = NULL, *tmp = NULL; @@ -225,10 +361,12 @@ UT_array *neu_node_manager_filter(neu_node_manager_t *mgr, int type, { if (!el->is_static && el->display) { if (el->adapter->module->type & type) { + // 按插件名称过滤 if (strlen(plugin) > 0 && strcmp(el->adapter->module->module_name, plugin) != 0) { continue; } + // 按节点名称过滤 if (strlen(node) > 0 && strstr(el->adapter->name, node) == NULL) { continue; @@ -244,9 +382,17 @@ UT_array *neu_node_manager_filter(neu_node_manager_t *mgr, int type, return array; } +/** + * @brief 获取所有节点信息 + * + * 获取系统中所有节点的信息列表,包括静态节点和不显示的节点 + * + * @param mgr 节点管理器对象 + * @return 所有节点信息数组 + */ UT_array *neu_node_manager_get_all(neu_node_manager_t *mgr) { - UT_array * array = NULL; + UT_array *array = NULL; UT_icd icd = { sizeof(neu_resp_node_info_t), NULL, NULL, NULL }; node_entity_t *el = NULL, *tmp = NULL; @@ -263,9 +409,18 @@ UT_array *neu_node_manager_get_all(neu_node_manager_t *mgr) return array; } +/** + * @brief 获取特定类型的适配器 + * + * 根据类型获取适配器对象列表,不包括静态节点和不显示的节点 + * + * @param mgr 节点管理器对象 + * @param type 节点类型过滤条件 + * @return 适配器指针数组 + */ UT_array *neu_node_manager_get_adapter(neu_node_manager_t *mgr, int type) { - UT_array * array = NULL; + UT_array *array = NULL; node_entity_t *el = NULL, *tmp = NULL; utarray_new(array, &ut_ptr_icd); @@ -282,6 +437,15 @@ UT_array *neu_node_manager_get_adapter(neu_node_manager_t *mgr, int type) return array; } +/** + * @brief 查找节点对应的适配器 + * + * 根据节点名称查找对应的适配器对象 + * + * @param mgr 节点管理器对象 + * @param name 节点名称 + * @return 成功返回适配器指针,失败返回NULL + */ neu_adapter_t *neu_node_manager_find(neu_node_manager_t *mgr, const char *name) { neu_adapter_t *adapter = NULL; @@ -295,6 +459,15 @@ neu_adapter_t *neu_node_manager_find(neu_node_manager_t *mgr, const char *name) return adapter; } +/** + * @brief 检查节点是否为单例节点 + * + * 判断指定节点是否为单例节点 + * + * @param mgr 节点管理器对象 + * @param name 节点名称 + * @return 如果是单例节点返回true,否则返回false + */ bool neu_node_manager_is_single(neu_node_manager_t *mgr, const char *name) { node_entity_t *node = NULL; @@ -307,6 +480,15 @@ bool neu_node_manager_is_single(neu_node_manager_t *mgr, const char *name) return false; } +/** + * @brief 检查节点是否为驱动类型 + * + * 判断指定节点是否为驱动节点 + * + * @param mgr 节点管理器对象 + * @param name 节点名称 + * @return 如果是驱动节点返回true,否则返回false + */ bool neu_node_manager_is_driver(neu_node_manager_t *mgr, const char *name) { node_entity_t *node = NULL; @@ -319,10 +501,19 @@ bool neu_node_manager_is_driver(neu_node_manager_t *mgr, const char *name) return false; } +/** + * @brief 获取特定类型节点的网络地址 + * + * 根据类型获取节点的网络地址列表,不包括静态节点 + * + * @param mgr 节点管理器对象 + * @param type 节点类型过滤条件 + * @return 网络地址数组 + */ UT_array *neu_node_manager_get_addrs(neu_node_manager_t *mgr, int type) { UT_icd icd = { sizeof(struct sockaddr_in), NULL, NULL, NULL }; - UT_array * addrs = NULL; + UT_array *addrs = NULL; node_entity_t *el = NULL, *tmp = NULL; utarray_new(addrs, &icd); @@ -340,10 +531,18 @@ UT_array *neu_node_manager_get_addrs(neu_node_manager_t *mgr, int type) return addrs; } +/** + * @brief 获取所有节点的网络地址 + * + * 获取所有节点的网络地址列表,包括静态节点 + * + * @param mgr 节点管理器对象 + * @return 所有节点的网络地址数组 + */ UT_array *neu_node_manager_get_addrs_all(neu_node_manager_t *mgr) { UT_icd icd = { sizeof(struct sockaddr_in), NULL, NULL, NULL }; - UT_array * addrs = NULL; + UT_array *addrs = NULL; node_entity_t *el = NULL, *tmp = NULL; utarray_new(addrs, &icd); @@ -357,11 +556,20 @@ UT_array *neu_node_manager_get_addrs_all(neu_node_manager_t *mgr) return addrs; } +/** + * @brief 获取指定节点的网络地址 + * + * 根据节点名称获取其网络地址 + * + * @param mgr 节点管理器对象 + * @param name 节点名称 + * @return 节点的网络地址,如果节点不存在则返回全零地址 + */ struct sockaddr_in neu_node_manager_get_addr(neu_node_manager_t *mgr, - const char * name) + const char *name) { struct sockaddr_in addr = { 0 }; - node_entity_t * node = NULL; + node_entity_t *node = NULL; HASH_FIND_STR(mgr->nodes, name, node); if (node != NULL) { @@ -371,6 +579,15 @@ struct sockaddr_in neu_node_manager_get_addr(neu_node_manager_t *mgr, return addr; } +/** + * @brief 检查节点是否为监控节点 + * + * 判断指定节点是否为监控节点 + * + * @param mgr 节点管理器对象 + * @param name 节点名称 + * @return 如果是监控节点返回true,否则返回false + */ bool neu_node_manager_is_monitor(neu_node_manager_t *mgr, const char *name) { node_entity_t *node = NULL; @@ -378,10 +595,20 @@ bool neu_node_manager_is_monitor(neu_node_manager_t *mgr, const char *name) return node ? node->is_monitor : false; } +/** + * @brief 遍历所有监控节点 + * + * 对每个监控节点执行回调函数 + * + * @param mgr 节点管理器对象 + * @param cb 回调函数,接收节点名称、网络地址和用户数据 + * @param data 传递给回调函数的用户数据 + * @return 成功返回0,如果回调函数返回非0值则中断遍历并返回该值 + */ int neu_node_manager_for_each_monitor(neu_node_manager_t *mgr, - int (*cb)(const char * name, + int (*cb)(const char *name, struct sockaddr_in addr, - void * data), + void *data), void *data) { int rv = 0; @@ -397,10 +624,18 @@ int neu_node_manager_for_each_monitor(neu_node_manager_t *mgr, return rv; } +/** + * @brief 获取所有节点的状态 + * + * 获取非静态且可显示节点的状态信息,包括运行状态、链接状态、日志级别和RTT值 + * + * @param mgr 节点管理器对象 + * @return 节点状态信息数组 + */ UT_array *neu_node_manager_get_state(neu_node_manager_t *mgr) { UT_icd icd = { sizeof(neu_nodes_state_t), NULL, NULL, NULL }; - UT_array * states = NULL; + UT_array *states = NULL; node_entity_t *el = NULL, *tmp = NULL; utarray_new(states, &icd); @@ -411,10 +646,14 @@ UT_array *neu_node_manager_get_state(neu_node_manager_t *mgr) if (!el->is_static && el->display) { strcpy(state.node, el->adapter->name); + // 获取运行状态 state.state.running = el->adapter->state; + // 获取连接状态 state.state.link = neu_plugin_to_plugin_common(el->adapter->plugin)->link_state; + // 获取日志级别 state.state.log_level = el->adapter->log_level; + // 获取RTT值 neu_metric_entry_t *e = NULL; if (NULL != el->adapter->metrics) { HASH_FIND_STR(el->adapter->metrics->entries, diff --git a/src/core/plugin_manager.c b/src/core/plugin_manager.c index 387844330..252eccb06 100644 --- a/src/core/plugin_manager.c +++ b/src/core/plugin_manager.c @@ -16,6 +16,14 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. **/ + +/** + * @file plugin_manager.c + * @brief 插件管理器实现 + * + * 本文件实现了Neuron系统的插件管理器,负责管理系统中的所有插件。 + * 插件管理器提供插件的加载、查找、创建实例和删除等功能。 + */ #include #include #include @@ -32,30 +40,52 @@ #include "argparse.h" #include "plugin_manager.h" +/** + * @brief 插件实体结构 + * + * 表示系统中的一个插件实体,包含插件名称、库名称和描述信息等 + */ typedef struct plugin_entity { - char *schema; - char *name; - char *lib_name; - char *description; - char *description_zh; + char *schema; ///< 插件模式 + char *name; ///< 插件名称 + char *lib_name; ///< 插件库文件名 + char *description; ///< 插件英文描述 + char *description_zh; ///< 插件中文描述 - neu_plugin_kind_e kind; - neu_node_type_e type; + neu_plugin_kind_e kind; ///< 插件类型(系统或自定义) + neu_node_type_e type; ///< 节点类型(驱动或应用) - bool display; + bool display; ///< 是否在界面上显示 - bool single; - char *single_name; + bool single; ///< 是否为单例插件 + char *single_name; ///< 单例插件名称 - UT_hash_handle hh; + UT_hash_handle hh; ///< 哈希表句柄,用于uthash } plugin_entity_t; +/** + * @brief 插件管理器结构 + * + * 管理系统中的所有插件实体 + */ struct neu_plugin_manager { - plugin_entity_t *plugins; + plugin_entity_t *plugins; ///< 插件哈希表 }; +/** + * @brief 释放插件实体资源 + * + * @param entity 插件实体指针 + */ static void entity_free(plugin_entity_t *entity); +/** + * @brief 创建插件管理器 + * + * 分配并初始化插件管理器对象 + * + * @return 返回新创建的插件管理器对象,失败则返回NULL + */ neu_plugin_manager_t *neu_plugin_manager_create() { neu_plugin_manager_t *manager = calloc(1, sizeof(neu_plugin_manager_t)); @@ -63,10 +93,18 @@ neu_plugin_manager_t *neu_plugin_manager_create() return manager; } +/** + * @brief 销毁插件管理器 + * + * 释放插件管理器及其管理的所有插件资源 + * + * @param mgr 插件管理器对象 + */ void neu_plugin_manager_destroy(neu_plugin_manager_t *mgr) { plugin_entity_t *el = NULL, *tmp = NULL; + // 遍历并释放所有插件实体 HASH_ITER(hh, mgr->plugins, el, tmp) { HASH_DEL(mgr->plugins, el); @@ -76,18 +114,29 @@ void neu_plugin_manager_destroy(neu_plugin_manager_t *mgr) free(mgr); } +/** + * @brief 添加插件 + * + * 加载指定的插件库,并添加到插件管理器中 + * + * @param mgr 插件管理器对象 + * @param plugin_lib_name 插件库文件名 + * @return 成功返回NEU_ERR_SUCCESS,失败返回错误码 + */ int neu_plugin_manager_add(neu_plugin_manager_t *mgr, - const char * plugin_lib_name) + const char *plugin_lib_name) { char lib_path[256] = { 0 }; - void * handle = NULL; - void * module = NULL; + void *handle = NULL; + void *module = NULL; neu_plugin_module_t *pm = NULL; - plugin_entity_t * plugin = NULL; + plugin_entity_t *plugin = NULL; assert(strlen(plugin_lib_name) <= NEU_PLUGIN_LIBRARY_LEN); + // 构建插件库路径 snprintf(lib_path, sizeof(lib_path) - 1, "./plugins/%s", plugin_lib_name); + // 加载动态库 handle = dlopen(lib_path, RTLD_NOW | RTLD_NODELETE); if (handle == NULL) { @@ -146,13 +195,25 @@ int neu_plugin_manager_add(neu_plugin_manager_t *mgr, return NEU_ERR_SUCCESS; } +/** + * @brief 删除插件 + * + * 从插件管理器中删除指定的插件,系统插件不允许删除 + * + * @param mgr 插件管理器对象 + * @param plugin_name 插件名称 + * @return + * 成功返回NEU_ERR_SUCCESS,系统插件返回NEU_ERR_LIBRARY_SYSTEM_NOT_ALLOW_DEL + */ int neu_plugin_manager_del(neu_plugin_manager_t *mgr, const char *plugin_name) { plugin_entity_t *plugin = NULL; int ret = NEU_ERR_LIBRARY_SYSTEM_NOT_ALLOW_DEL; + // 查找插件 HASH_FIND_STR(mgr->plugins, plugin_name, plugin); if (plugin != NULL) { + // 只允许删除非系统插件 if (plugin->kind != NEU_PLUGIN_KIND_SYSTEM) { HASH_DEL(mgr->plugins, plugin); entity_free(plugin); @@ -163,15 +224,24 @@ int neu_plugin_manager_del(neu_plugin_manager_t *mgr, const char *plugin_name) return ret; } +/** + * @brief 获取非单例插件列表 + * + * 获取系统中所有非单例插件的信息列表 + * + * @param mgr 插件管理器对象 + * @return 非单例插件信息数组 + */ UT_array *neu_plugin_manager_get(neu_plugin_manager_t *mgr) { - UT_array * plugins; + UT_array *plugins; UT_icd icd = { sizeof(neu_resp_plugin_info_t), NULL, NULL, NULL }; plugin_entity_t *el = NULL, *tmp = NULL; utarray_new(plugins, &icd); HASH_ITER(hh, mgr->plugins, el, tmp) { + // 跳过单例插件 if (el->single) { continue; } @@ -194,15 +264,24 @@ UT_array *neu_plugin_manager_get(neu_plugin_manager_t *mgr) return plugins; } +/** + * @brief 获取单例插件列表 + * + * 获取系统中所有单例插件的信息列表 + * + * @param mgr 插件管理器对象 + * @return 单例插件信息数组 + */ UT_array *neu_plugin_manager_get_single(neu_plugin_manager_t *mgr) { - UT_array * plugins; + UT_array *plugins; UT_icd icd = { sizeof(neu_resp_plugin_info_t), NULL, NULL, NULL }; plugin_entity_t *el = NULL, *tmp = NULL; utarray_new(plugins, &icd); HASH_ITER(hh, mgr->plugins, el, tmp) { + // 只处理单例插件 if (el->single) { neu_resp_plugin_info_t info = { .kind = el->kind, @@ -229,12 +308,23 @@ UT_array *neu_plugin_manager_get_single(neu_plugin_manager_t *mgr) return plugins; } +/** + * @brief 查找插件 + * + * 根据插件名称查找插件信息 + * + * @param mgr 插件管理器对象 + * @param plugin_name 插件名称 + * @param info 用于返回插件信息的结构体指针 + * @return 成功返回0,失败返回-1 + */ int neu_plugin_manager_find(neu_plugin_manager_t *mgr, const char *plugin_name, neu_resp_plugin_info_t *info) { plugin_entity_t *plugin = NULL; int ret = -1; + // 查找插件 HASH_FIND_STR(mgr->plugins, plugin_name, plugin); if (plugin != NULL) { ret = 0; @@ -243,6 +333,7 @@ int neu_plugin_manager_find(neu_plugin_manager_t *mgr, const char *plugin_name, info->type = plugin->type; info->kind = plugin->kind; + // 如果是单例插件,复制单例名称 if (plugin->single_name != NULL) { strcpy(info->single_name, plugin->single_name); } @@ -255,16 +346,34 @@ int neu_plugin_manager_find(neu_plugin_manager_t *mgr, const char *plugin_name, return ret; } +/** + * @brief 检查插件是否存在 + * + * 判断指定名称的插件是否存在 + * + * @param mgr 插件管理器对象 + * @param plugin_name 插件名称 + * @return 如果插件存在返回true,否则返回false + */ bool neu_plugin_manager_exists(neu_plugin_manager_t *mgr, - const char * plugin_name) + const char *plugin_name) { plugin_entity_t *plugin = NULL; HASH_FIND_STR(mgr->plugins, plugin_name, plugin); return NULL != plugin; } +/** + * @brief 检查插件是否为单例插件 + * + * 判断指定插件是否为单例插件 + * + * @param mgr 插件管理器对象 + * @param plugin_name 插件名称 + * @return 如果是单例插件返回true,否则返回false + */ bool neu_plugin_manager_is_single(neu_plugin_manager_t *mgr, - const char * plugin_name) + const char *plugin_name) { plugin_entity_t *plugin = NULL; HASH_FIND_STR(mgr->plugins, plugin_name, plugin); @@ -276,21 +385,35 @@ bool neu_plugin_manager_is_single(neu_plugin_manager_t *mgr, return plugin->single; } -int neu_plugin_manager_create_instance(neu_plugin_manager_t * mgr, - const char * plugin_name, +/** + * @brief 创建插件实例 + * + * 根据插件名称加载插件库并创建插件实例 + * + * @param mgr 插件管理器对象 + * @param plugin_name 插件名称 + * @param instance 用于返回插件实例的指针 + * @return 成功返回0,失败返回-1 + */ +int neu_plugin_manager_create_instance(neu_plugin_manager_t *mgr, + const char *plugin_name, neu_plugin_instance_t *instance) { plugin_entity_t *plugin = NULL; + // 查找插件 HASH_FIND_STR(mgr->plugins, plugin_name, plugin); if (plugin != NULL) { char lib_path[256] = { 0 }; + // 构建插件库路径 snprintf(lib_path, sizeof(lib_path) - 1, "%s/%s", g_plugin_dir, plugin->lib_name); + // 加载动态库 instance->handle = dlopen(lib_path, RTLD_NOW | RTLD_NODELETE); assert(instance->handle != NULL); + // 获取插件模块指针 instance->module = (neu_plugin_module_t *) dlsym(instance->handle, "neu_plugin_module"); assert(instance->module != NULL); @@ -300,37 +423,65 @@ int neu_plugin_manager_create_instance(neu_plugin_manager_t * mgr, return -1; } -void neu_plugin_manager_load_static(neu_plugin_manager_t * mgr, - const char * plugin_name, +/** + * @brief 加载静态插件 + * + * 加载系统内置的静态插件(不需要动态库) + * + * @param mgr 插件管理器对象(未使用) + * @param plugin_name 插件名称 + * @param instance 用于返回插件实例的指针 + */ +void neu_plugin_manager_load_static(neu_plugin_manager_t *mgr, + const char *plugin_name, neu_plugin_instance_t *instance) { (void) mgr; instance->handle = NULL; + // 为仪表板插件设置静态模块 if (strcmp(DEFAULT_DASHBOARD_PLUGIN_NAME, plugin_name) == 0) { instance->module = (neu_plugin_module_t *) &default_dashboard_plugin_module; } } -void neu_plugin_manager_destroy_instance(neu_plugin_manager_t * mgr, +/** + * @brief 销毁插件实例 + * + * 关闭插件动态库,释放插件实例资源 + * + * @param mgr 插件管理器对象(未使用) + * @param instance 要销毁的插件实例 + */ +void neu_plugin_manager_destroy_instance(neu_plugin_manager_t *mgr, neu_plugin_instance_t *instance) { (void) mgr; nlog_notice("destroy plugin instance: %s, handle: %p", instance->module->module_name, instance->handle); + // 如果有动态库句柄,关闭动态库 if (instance->handle != NULL) { dlclose(instance->handle); } } +/** + * @brief 释放插件实体资源 + * + * 释放插件实体结构体及其所有分配的内存 + * + * @param entity 插件实体指针 + */ static void entity_free(plugin_entity_t *entity) { nlog_notice("del plugin, name: %s, library: %s, kind: %d, type: %d", entity->name, entity->lib_name, entity->kind, entity->type); + // 释放单例插件名称 if (entity->single) { free(entity->single_name); } + // 释放所有字符串资源 free(entity->schema); free(entity->name); free(entity->lib_name); diff --git a/src/core/storage.c b/src/core/storage.c index bce44ea6f..f6d1c7065 100644 --- a/src/core/storage.c +++ b/src/core/storage.c @@ -16,64 +16,125 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. **/ + +/** + * @file storage.c + * @brief 存储管理模块,负责Neuron系统的持久化数据管理 + * + * 该文件实现了与存储相关的函数,包括插件、节点、订阅关系、模板等数据的持久化存储和加载。 + * 它提供了一组接口,用于将内存中的配置数据保存到磁盘,以及在系统启动时从磁盘加载配置数据。 + * + * 主要功能: + * 1. 插件信息的存储与加载 + * 2. 节点(适配器)信息的存储、更新、删除与加载 + * 3. 订阅关系的存储、更新、删除与加载 + * 4. 模板及其组和标签的存储、更新、删除与加载 + */ #include "errcodes.h" #include "utils/log.h" #include "adapter/storage.h" #include "storage.h" +/** + * @brief 存储所有插件信息 + * + * 从管理器获取所有插件信息,并将其保存到持久化存储中 + * + * @param manager 管理器对象指针 + */ void manager_strorage_plugin(neu_manager_t *manager) { UT_array *plugin_infos = NULL; + // 获取所有插件信息 plugin_infos = neu_manager_get_plugins(manager); + // 将插件信息存储到持久化存储 int rv = neu_persister_store_plugins(plugin_infos); if (0 != rv) { nlog_error("failed to store plugins infos"); } + // 释放插件信息列表 neu_persist_plugin_infos_free(plugin_infos); return; } +/** + * @brief 添加节点信息到持久化存储 + * + * 获取指定节点的信息,并将其保存到持久化存储中 + * + * @param manager 管理器对象指针 + * @param node 节点名称 + */ void manager_storage_add_node(neu_manager_t *manager, const char *node) { int rv = 0; neu_persist_node_info_t node_info = {}; + // 获取节点信息 rv = neu_manager_get_node_info(manager, node, &node_info); if (0 != rv) { nlog_error("unable to get adapter:%s info", node); return; } + // 存储节点信息 rv = neu_persister_store_node(&node_info); if (0 != rv) { nlog_error("failed to store adapter info"); } + // 释放节点信息资源 neu_persist_node_info_fini(&node_info); } +/** + * @brief 更新节点信息 + * + * 更新节点名称,将旧节点名称更改为新节点名称 + * + * @param manager 管理器对象指针(未使用) + * @param node 原节点名称 + * @param new_name 新节点名称 + */ void manager_storage_update_node(neu_manager_t *manager, const char *node, const char *new_name) { - (void) manager; + (void) manager; // 未使用的参数 neu_persister_update_node(node, new_name); } +/** + * @brief 从持久化存储中删除节点信息 + * + * @param manager 管理器对象指针(未使用) + * @param node 要删除的节点名称 + */ void manager_storage_del_node(neu_manager_t *manager, const char *node) { - (void) manager; + (void) manager; // 未使用的参数 neu_persister_delete_node(node); } +/** + * @brief 存储订阅关系 + * + * 将应用节点对驱动节点组的订阅关系保存到持久化存储 + * + * @param manager 管理器对象指针(未使用) + * @param app 应用节点名称 + * @param driver 驱动节点名称 + * @param group 订阅的组名 + * @param params 订阅参数 + */ void manager_storage_subscribe(neu_manager_t *manager, const char *app, const char *driver, const char *group, const char *params) { - (void) manager; + (void) manager; // 未使用的参数 int rv = neu_persister_store_subscription(app, driver, group, params); if (0 != rv) { nlog_error("fail store subscription app:%s driver:%s group:%s", app, @@ -81,11 +142,22 @@ void manager_storage_subscribe(neu_manager_t *manager, const char *app, } } +/** + * @brief 更新订阅关系 + * + * 更新应用节点对驱动节点组的订阅参数 + * + * @param manager 管理器对象指针(未使用) + * @param app 应用节点名称 + * @param driver 驱动节点名称 + * @param group 订阅的组名 + * @param params 更新后的订阅参数 + */ void manager_storage_update_subscribe(neu_manager_t *manager, const char *app, const char *driver, const char *group, const char *params) { - (void) manager; + (void) manager; // 未使用的参数 int rv = neu_persister_update_subscription(app, driver, group, params); if (0 != rv) { nlog_error("fail update subscription app:%s driver:%s group:%s", app, @@ -93,10 +165,20 @@ void manager_storage_update_subscribe(neu_manager_t *manager, const char *app, } } +/** + * @brief 删除订阅关系 + * + * 从持久化存储中删除应用节点对驱动节点组的订阅关系 + * + * @param manager 管理器对象指针(未使用) + * @param app 应用节点名称 + * @param driver 驱动节点名称 + * @param group 订阅的组名 + */ void manager_storage_unsubscribe(neu_manager_t *manager, const char *app, const char *driver, const char *group) { - (void) manager; + (void) manager; // 未使用的参数 int rv = neu_persister_delete_subscription(app, driver, group); if (0 != rv) { nlog_error("fail delete subscription app:%s driver:%s group:%s", app, @@ -104,6 +186,16 @@ void manager_storage_unsubscribe(neu_manager_t *manager, const char *app, } } +/** + * @brief 添加北向驱动映射关系 + * + * 将北向驱动对南向驱动组的映射关系保存到持久化存储,实际调用subscribe函数 + * + * @param manager 管理器对象指针 + * @param ndriver 北向驱动节点名称 + * @param driver 南向驱动节点名称 + * @param group 订阅的组名 + */ void manager_storage_add_ndriver_map(neu_manager_t *manager, const char *ndriver, const char *driver, const char *group) @@ -111,6 +203,16 @@ void manager_storage_add_ndriver_map(neu_manager_t *manager, return manager_storage_subscribe(manager, ndriver, driver, group, NULL); } +/** + * @brief 删除北向驱动映射关系 + * + * 从持久化存储中删除北向驱动对南向驱动组的映射关系,实际调用unsubscribe函数 + * + * @param manager 管理器对象指针 + * @param ndriver 北向驱动节点名称 + * @param driver 南向驱动节点名称 + * @param group 订阅的组名 + */ void manager_storage_del_ndriver_map(neu_manager_t *manager, const char *ndriver, const char *driver, const char *group) @@ -118,28 +220,50 @@ void manager_storage_del_ndriver_map(neu_manager_t *manager, return manager_storage_unsubscribe(manager, ndriver, driver, group); } +/** + * @brief 保存节点的组和标签信息 + * + * 用于遍历组并保存其信息的回调函数,从组中提取信息并保存到存储系统中 + * + * @param grp 组对象指针 + * @param data 用户数据,实际上是节点名称 + * @return 成功返回0,失败返回错误码 + */ static int save_node_group_and_tags(neu_group_t *grp, void *data) { const char *node = data; const char *name = neu_group_get_name(grp); uint32_t interval = neu_group_get_interval(grp); + // 获取组内所有标签 UT_array *tags = neu_group_get_tag(grp); if (NULL == tags) { return NEU_ERR_EINTERNAL; } + // 保存组和标签信息 adapter_storage_add_group(node, name, interval); adapter_storage_add_tags(node, name, utarray_front(tags), utarray_len(tags)); + // 释放标签数组 utarray_free(tags); return 0; } +/** + * @brief 存储从模板实例化的节点信息 + * + * 将基于模板创建的节点及其组和标签信息保存到持久化存储 + * + * @param manager 管理器对象指针 + * @param tmpl_name 模板名称 + * @param node 实例化的节点名称 + */ void manager_storage_inst_node(neu_manager_t *manager, const char *tmpl_name, const char *node) { + // 查找模板 neu_template_t *tmpl = neu_template_manager_find(manager->template_manager, tmpl_name); if (NULL == tmpl) { @@ -148,8 +272,10 @@ void manager_storage_inst_node(neu_manager_t *manager, const char *tmpl_name, return; } + // 保存节点基本信息 manager_storage_add_node(manager, node); + // 遍历所有组,保存组和标签信息 int rv = neu_template_for_each_group(tmpl, save_node_group_and_tags, (void *) node); if (0 != rv) { @@ -157,27 +283,48 @@ void manager_storage_inst_node(neu_manager_t *manager, const char *tmpl_name, } } +/** + * @brief 保存模板的组和标签信息 + * + * 用于遍历模板组并保存其信息的回调函数,从组中提取信息并保存到存储系统中 + * + * @param grp 组对象指针 + * @param data 用户数据,实际上是模板名称 + * @return 成功返回0,失败返回错误码 + */ static int save_tmpl_group_and_tags(neu_group_t *grp, void *data) { const char *tmpl_name = data; const char *name = neu_group_get_name(grp); uint32_t interval = neu_group_get_interval(grp); + // 获取组内所有标签 UT_array *tags = neu_group_get_tag(grp); if (NULL == tags) { return NEU_ERR_EINTERNAL; } + // 保存模板组和标签信息 manager_storage_add_template_group(tmpl_name, name, interval); manager_storage_add_template_tags(tmpl_name, name, utarray_front(tags), utarray_len(tags)); + // 释放标签数组 utarray_free(tags); return 0; } +/** + * @brief 添加模板信息到持久化存储 + * + * 将模板及其组和标签信息保存到持久化存储 + * + * @param manager 管理器对象指针 + * @param tmpl_name 模板名称 + */ void manager_storage_add_template(neu_manager_t *manager, const char *tmpl_name) { + // 查找模板 neu_template_t *tmpl = neu_template_manager_find(manager->template_manager, tmpl_name); if (NULL == tmpl) { @@ -185,12 +332,14 @@ void manager_storage_add_template(neu_manager_t *manager, const char *tmpl_name) return; } + // 保存模板基本信息 int rv = neu_persister_store_template(tmpl_name, neu_template_plugin(tmpl)); if (0 != rv) { nlog_error("failed to store template info"); return; } + // 遍历所有组,保存组和标签信息 rv = neu_template_for_each_group(tmpl, save_tmpl_group_and_tags, (void *) tmpl_name); if (0 != rv) { @@ -198,9 +347,15 @@ void manager_storage_add_template(neu_manager_t *manager, const char *tmpl_name) } } +/** + * @brief 从持久化存储中删除模板信息 + * + * @param manager 管理器对象指针(未使用) + * @param tmpl_name 要删除的模板名称 + */ void manager_storage_del_template(neu_manager_t *manager, const char *tmpl_name) { - (void) manager; + (void) manager; // 未使用的参数 int rv = neu_persister_delete_template(tmpl_name); if (0 != rv) { @@ -208,9 +363,16 @@ void manager_storage_del_template(neu_manager_t *manager, const char *tmpl_name) } } +/** + * @brief 清除所有模板信息 + * + * 从持久化存储中删除所有模板信息 + * + * @param manager 管理器对象指针(未使用) + */ void manager_storage_clear_templates(neu_manager_t *manager) { - (void) manager; + (void) manager; // 未使用的参数 int rv = neu_persister_clear_templates(); if (0 != rv) { @@ -218,14 +380,23 @@ void manager_storage_clear_templates(neu_manager_t *manager) } } +/** + * @brief 添加模板组信息到持久化存储 + * + * @param tmpl 模板名称 + * @param group 组名称 + * @param interval 采集间隔时间 + */ void manager_storage_add_template_group(const char *tmpl, const char *group, uint32_t interval) { + // 构建组信息结构 neu_persist_group_info_t info = { .name = (char *) group, .interval = interval, }; + // 存储模板组信息 int rv = neu_persister_store_template_group(tmpl, &info); if (0 != rv) { nlog_error("store template:%s grp:%s, interval:%" PRIu32 " fail", tmpl, @@ -233,15 +404,27 @@ void manager_storage_add_template_group(const char *tmpl, const char *group, } } +/** + * @brief 更新模板组信息 + * + * 更新模板组的名称和采集间隔 + * + * @param tmpl 模板名称 + * @param group 原组名称 + * @param new_name 新组名称 + * @param interval 更新后的采集间隔时间 + */ void manager_storage_update_template_group(const char *tmpl, const char *group, const char *new_name, uint32_t interval) { + // 构建更新后的组信息结构 neu_persist_group_info_t info = { .name = (char *) new_name, .interval = interval, }; + // 更新模板组信息 int rv = neu_persister_update_template_group(tmpl, group, &info); if (0 != rv) { nlog_error("update template:%s group:%s, new_name:%s interval:%" PRIu32 @@ -250,14 +433,27 @@ void manager_storage_update_template_group(const char *tmpl, const char *group, } } +/** + * @brief 从持久化存储中删除模板组信息 + * + * @param tmpl 模板名称 + * @param group 要删除的组名称 + */ void manager_storage_del_template_group(const char *tmpl, const char *group) { int rv = neu_persister_delete_template_group(tmpl, group); if (0 != rv) { - nlog_error("delete template:%s grp:%s fail" PRIu32, tmpl, group); + nlog_error("delete template:%s grp:%s fail", tmpl, group); } } +/** + * @brief 添加模板标签到持久化存储 + * + * @param tmpl 模板名称 + * @param group 组名称 + * @param tag 标签数据指针 + */ void manager_storage_add_template_tag(const char *tmpl, const char *group, const neu_datatag_t *tag) { @@ -268,17 +464,28 @@ void manager_storage_add_template_tag(const char *tmpl, const char *group, } } +/** + * @brief 批量添加模板标签到持久化存储 + * + * @param tmpl 模板名称 + * @param group 组名称 + * @param tags 标签数组指针 + * @param n 标签数量 + */ void manager_storage_add_template_tags(const char *tmpl, const char *group, const neu_datatag_t *tags, size_t n) { + // 处理边界情况 if (0 == n) { return; } + // 如果只有一个标签,调用单标签添加函数 if (1 == n) { return manager_storage_add_template_tag(tmpl, group, &tags[0]); } + // 批量存储标签 int rv = neu_persister_store_template_tags(tmpl, group, tags, n); if (0 != rv) { nlog_error("store %zu tags:[%s ... %s] template:%s grp:%s fail", n, @@ -286,6 +493,13 @@ void manager_storage_add_template_tags(const char *tmpl, const char *group, } } +/** + * @brief 更新模板标签 + * + * @param tmpl 模板名称 + * @param group 组名称 + * @param tag 更新后的标签数据指针 + */ void manager_storage_update_template_tag(const char *tmpl, const char *group, const neu_datatag_t *tag) { @@ -296,17 +510,28 @@ void manager_storage_update_template_tag(const char *tmpl, const char *group, } } +/** + * @brief 批量更新模板标签 + * + * @param tmpl 模板名称 + * @param group 组名称 + * @param tags 标签数组指针 + * @param n 标签数量 + */ void manager_storage_update_template_tags(const char *tmpl, const char *group, const neu_datatag_t *tags, size_t n) { + // 处理边界情况 if (0 == n) { return; } + // 如果只有一个标签,调用单标签更新函数 if (1 == n) { return manager_storage_update_template_tag(tmpl, group, &tags[0]); } + // 批量更新标签 int rv = neu_persister_update_template_tags(tmpl, group, tags, n); if (0 != rv) { nlog_error("update %zu tags:[%s ... %s] template:%s grp:%s fail", n, @@ -314,6 +539,15 @@ void manager_storage_update_template_tags(const char *tmpl, const char *group, } } +/** + * @brief 删除模板标签 + * + * 从持久化存储中删除指定的模板标签 + * + * @param tmpl 模板名称 + * @param group 组名称 + * @param tag 要删除的标签名称 + */ void manager_storage_del_template_tag(const char *tmpl, const char *group, const char *tag) { @@ -323,17 +557,30 @@ void manager_storage_del_template_tag(const char *tmpl, const char *group, } } +/** + * @brief 批量删除模板标签 + * + * 从持久化存储中批量删除指定的模板标签 + * + * @param tmpl 模板名称 + * @param group 组名称 + * @param tags 要删除的标签名称数组 + * @param n 标签数量 + */ void manager_storage_del_template_tags(const char *tmpl, const char *group, const char *const *tags, size_t n) { + // 处理边界情况 if (0 == n) { return; } + // 如果只有一个标签,调用单标签删除函数 if (1 == n) { return manager_storage_del_template_tag(tmpl, group, tags[0]); } + // 批量删除标签 int rv = neu_persister_delete_template_tags(tmpl, group, tags, n); if (0 != rv) { nlog_error("del %zu tags:[%s ... %s] template:%s grp:%s fail", n, @@ -341,15 +588,25 @@ void manager_storage_del_template_tags(const char *tmpl, const char *group, } } +/** + * @brief 从持久化存储加载插件 + * + * 从持久化存储中加载所有插件信息并添加到管理器中 + * + * @param manager 管理器对象指针 + * @return 成功返回0,失败返回错误码 + */ int manager_load_plugin(neu_manager_t *manager) { UT_array *plugin_infos = NULL; + // 从持久化存储加载插件信息 int rv = neu_persister_load_plugins(&plugin_infos); if (rv != 0) { return rv; } + // 遍历插件信息,添加到管理器 utarray_foreach(plugin_infos, char **, name) { rv = neu_manager_add_plugin(manager, *name); @@ -357,62 +614,92 @@ int manager_load_plugin(neu_manager_t *manager) nlog_notice("load plugin %s, lib:%s", ok_or_err, *name); } - utarray_foreach(plugin_infos, char **, name) { free(*name); } + // 释放资源 + utarray_foreach(plugin_infos, char **, name) + { + free(*name); + } utarray_free(plugin_infos); return rv; } +/** + * @brief 从持久化存储加载节点 + * + * 从持久化存储中加载所有节点信息并添加到管理器中 + * + * @param manager 管理器对象指针 + * @return 成功返回0,失败返回错误码 + */ int manager_load_node(neu_manager_t *manager) { UT_array *node_infos = NULL; int rv = 0; + // 从持久化存储加载节点信息 rv = neu_persister_load_nodes(&node_infos); if (0 != rv) { nlog_error("failed to load adapter infos"); return -1; } + // 遍历节点信息,添加到管理器 utarray_foreach(node_infos, neu_persist_node_info_t *, node_info) { rv = neu_manager_add_node(manager, node_info->name, - node_info->plugin_name, node_info->state, - true); + node_info->plugin_name, node_info->state, + true); const char *ok_or_err = (0 == rv) ? "success" : "fail"; nlog_notice("load adapter %s type:%d, name:%s plugin:%s state:%d", ok_or_err, node_info->type, node_info->name, node_info->plugin_name, node_info->state); } + // 释放资源 utarray_free(node_infos); return rv; } +/** + * @brief 从持久化存储加载订阅关系 + * + * 从持久化存储中加载所有应用节点的订阅关系并重新建立订阅 + * + * @param manager 管理器对象指针 + * @return 始终返回0 + */ int manager_load_subscribe(neu_manager_t *manager) { + // 获取所有应用节点和北向驱动节点 UT_array *nodes = neu_node_manager_get( manager->node_manager, NEU_NA_TYPE_APP | NEU_NA_TYPE_NDRIVER); + // 遍历所有节点 utarray_foreach(nodes, neu_resp_node_info_t *, node) { int rv = 0; UT_array *sub_infos = NULL; + // 加载该节点的所有订阅信息 rv = neu_persister_load_subscriptions(node->node, &sub_infos); if (0 != rv) { nlog_warn("load %s subscribetion infos error", node->node); } else { + // 遍历所有订阅信息,重新建立订阅关系 utarray_foreach(sub_infos, neu_persist_subscription_info_t *, info) { uint16_t app_port = 0; - rv = neu_manager_subscribe(manager, node->node, + // 建立订阅关系 + rv = neu_manager_subscribe(manager, node->node, info->driver_name, info->group_name, info->params, &app_port); const char *ok_or_err = (0 == rv) ? "success" : "fail"; nlog_notice("%s load subscription app:%s driver:%s grp:%s", ok_or_err, node->node, info->driver_name, info->group_name); + + // 如果订阅成功,发送订阅消息 if (0 == rv) { neu_manager_send_subscribe( manager, node->node, info->driver_name, @@ -420,31 +707,46 @@ int manager_load_subscribe(neu_manager_t *manager) } } + // 释放订阅信息资源 utarray_free(sub_infos); } } + // 释放节点信息资源 utarray_free(nodes); return 0; } +/** + * @brief 加载模板的组和标签信息 + * + * 从持久化存储中加载指定模板的所有组和标签信息,并添加到管理器中 + * + * @param manager 管理器对象指针 + * @param tmpl_name 模板名称 + * @return 成功返回0,失败返回错误码 + */ static int load_template_group_and_tags(neu_manager_t *manager, - const char * tmpl_name) + const char *tmpl_name) { int rv = 0; UT_array *group_infos = NULL; + // 加载模板的所有组信息 rv = neu_persister_load_template_groups(tmpl_name, &group_infos); if (0 != rv) { nlog_warn("load template %s group fail", tmpl_name); return rv; } + // 遍历所有组信息 utarray_foreach(group_infos, neu_persist_group_info_t *, p) { + // 添加组到模板 neu_manager_add_template_group(manager, tmpl_name, p->name, p->interval); + // 加载该组的所有标签信息 UT_array *tags = NULL; rv = neu_persister_load_template_tags(tmpl_name, p->name, &tags); if (0 != rv) { @@ -453,42 +755,58 @@ static int load_template_group_and_tags(neu_manager_t *manager, continue; } + // 添加标签到模板组 neu_manager_add_template_tags(manager, tmpl_name, p->name, utarray_len(tags), utarray_front(tags), NULL); + // 释放标签信息资源 utarray_free(tags); } + // 释放组信息资源 utarray_free(group_infos); return rv; } +/** + * @brief 从持久化存储加载模板 + * + * 从持久化存储中加载所有模板信息及其组和标签信息,并添加到管理器中 + * + * @param manager 管理器对象指针 + * @return 成功返回0,失败返回错误码 + */ int manager_load_template(neu_manager_t *manager) { int rv = 0; UT_array *tmpl_infos = NULL; + // 加载所有模板信息 rv = neu_persister_load_templates(&tmpl_infos); if (0 != rv) { nlog_error("failed to load template infos"); return -1; } + // 遍历所有模板信息 utarray_foreach(tmpl_infos, neu_persist_template_info_t *, tmpl_info) { + // 添加模板到管理器 rv = neu_manager_add_template(manager, tmpl_info->name, tmpl_info->plugin_name, 0, NULL); const char *ok_or_err = (0 == rv) ? "success" : "fail"; nlog_info("load template name:%s plugin:%s %s", tmpl_info->name, tmpl_info->plugin_name, ok_or_err); if (0 != rv) { - continue; // ignore error, load as much data as possible + continue; // 忽略错误,尽可能加载更多数据 } + // 加载该模板的所有组和标签信息 load_template_group_and_tags(manager, tmpl_info->name); } + // 释放模板信息资源 utarray_free(tmpl_infos); return rv; } diff --git a/src/core/subscribe.c b/src/core/subscribe.c index df38ce96c..4b594008b 100644 --- a/src/core/subscribe.c +++ b/src/core/subscribe.c @@ -16,6 +16,15 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. **/ + +/** + * @file subscribe.c + * @brief 订阅管理模块,负责管理Neuron系统中的订阅关系 + * + * 该文件实现了订阅管理器,用于管理应用节点对驱动节点数据组的订阅关系。 + * 订阅管理器提供了创建、查找、添加、更新和删除订阅关系的功能。 + * 订阅关系是Neuron系统中数据流转的基础,应用节点通过订阅驱动节点的数据组来接收数据。 + */ #include #include "adapter.h" @@ -24,26 +33,51 @@ #include "subscribe.h" +/** + * @brief 订阅元素键结构体 + * + * 用于在哈希表中唯一标识一个数据源(驱动+组) + */ typedef struct sub_elem_key { - char driver[NEU_NODE_NAME_LEN]; - char group[NEU_GROUP_NAME_LEN]; + char driver[NEU_NODE_NAME_LEN]; /**< 驱动节点名称 */ + char group[NEU_GROUP_NAME_LEN]; /**< 组名称 */ } sub_elem_key_t; +/** + * @brief 订阅元素结构体 + * + * 表示一个数据源(驱动+组)的所有订阅应用信息 + */ typedef struct sub_elem { - sub_elem_key_t key; + sub_elem_key_t key; /**< 订阅键(驱动+组) */ - UT_array *apps; + UT_array *apps; /**< 订阅该数据源的应用列表 */ - UT_hash_handle hh; + UT_hash_handle hh; /**< UT哈希表句柄 */ } sub_elem_t; +/** + * @brief 应用订阅数组元素操作描述符 + */ static const UT_icd app_sub_icd = { sizeof(neu_app_subscribe_t), NULL, NULL, NULL }; +/** + * @brief 订阅管理器结构体 + * + * 管理所有订阅关系的核心数据结构 + */ struct neu_subscribe_mgr { - sub_elem_t *ss; + sub_elem_t *ss; /**< 订阅元素哈希表 */ }; +/** + * @brief 创建订阅管理器 + * + * 分配并初始化一个新的订阅管理器实例 + * + * @return 新创建的订阅管理器指针 + */ neu_subscribe_mgr_t *neu_subscribe_manager_create() { neu_subscribe_mgr_t *mgr = calloc(1, sizeof(neu_subscribe_mgr_t)); @@ -51,52 +85,92 @@ neu_subscribe_mgr_t *neu_subscribe_manager_create() return mgr; } +/** + * @brief 销毁订阅管理器 + * + * 释放订阅管理器及其所有资源,包括所有订阅元素和应用订阅信息 + * + * @param mgr 要销毁的订阅管理器指针 + */ void neu_subscribe_manager_destroy(neu_subscribe_mgr_t *mgr) { sub_elem_t *el = NULL, *tmp = NULL; + // 遍历哈希表中的所有元素 HASH_ITER(hh, mgr->ss, el, tmp) { + // 从哈希表中删除当前元素 HASH_DEL(mgr->ss, el); + // 释放每个应用订阅的资源 utarray_foreach(el->apps, neu_app_subscribe_t *, sub_app) { neu_app_subscribe_fini(sub_app); } + // 释放应用数组 utarray_free(el->apps); + // 释放元素本身 free(el); } + // 释放管理器 free(mgr); } +/** + * @brief 查找指定驱动和组的订阅应用 + * + * 根据驱动名称和组名称查找所有订阅该数据源的应用 + * + * @param mgr 订阅管理器指针 + * @param driver 驱动节点名称 + * @param group 组名称 + * @return 包含所有订阅应用的数组,如果没有找到则返回NULL + * @note 返回的数组需要由调用者释放 + */ UT_array *neu_subscribe_manager_find(neu_subscribe_mgr_t *mgr, const char *driver, const char *group) { - sub_elem_t * find = NULL; + sub_elem_t *find = NULL; sub_elem_key_t key = { 0 }; + // 构建查找键 strncpy(key.driver, driver, sizeof(key.driver)); strncpy(key.group, group, sizeof(key.group)); + // 在哈希表中查找 HASH_FIND(hh, mgr->ss, &key, sizeof(sub_elem_key_t), find); if (find) { + // 找到后返回应用列表的克隆 return utarray_clone(find->apps); } else { return NULL; } } +/** + * @brief 查找指定驱动的所有订阅应用 + * + * 根据驱动名称查找所有订阅该驱动任意组的应用 + * + * @param mgr 订阅管理器指针 + * @param driver 驱动节点名称 + * @return 包含所有订阅应用的数组,即使没有找到也会返回空数组 + * @note 返回的数组需要由调用者释放 + */ UT_array *neu_subscribe_manager_find_by_driver(neu_subscribe_mgr_t *mgr, - const char * driver) + const char *driver) { sub_elem_t *el = NULL, *tmp = NULL; - UT_array * apps = NULL; + UT_array *apps = NULL; + // 创建新的应用数组 utarray_new(apps, &app_sub_icd); + // 遍历哈希表中的所有元素 HASH_ITER(hh, mgr->ss, el, tmp) { + // 如果驱动名称匹配,将该元素的所有应用添加到结果数组 if (strcmp(driver, el->key.driver) == 0) { utarray_concat(apps, el->apps); } @@ -105,25 +179,42 @@ UT_array *neu_subscribe_manager_find_by_driver(neu_subscribe_mgr_t *mgr, return apps; } +/** + * @brief 获取指定应用的所有订阅信息 + * + * 根据应用名称获取其所有订阅的驱动和组信息 + * + * @param mgr 订阅管理器指针 + * @param app 应用节点名称 + * @return 包含所有订阅信息的数组,即使没有找到也会返回空数组 + * @note 返回的数组需要由调用者释放 + */ UT_array *neu_subscribe_manager_get(neu_subscribe_mgr_t *mgr, const char *app) { sub_elem_t *el = NULL, *tmp = NULL; - UT_array * groups = NULL; + UT_array *groups = NULL; UT_icd icd = { sizeof(neu_resp_subscribe_info_t), NULL, NULL, NULL }; + // 创建新的订阅信息数组 utarray_new(groups, &icd); + + // 遍历哈希表中的所有元素 HASH_ITER(hh, mgr->ss, el, tmp) { + // 检查每个元素中的应用列表 utarray_foreach(el->apps, neu_app_subscribe_t *, sub_app) { + // 如果找到匹配的应用名称 if (strcmp(sub_app->app_name, app) == 0) { neu_resp_subscribe_info_t info = { 0 }; + // 填充订阅信息 strncpy(info.driver, el->key.driver, sizeof(info.driver)); strncpy(info.app, app, sizeof(info.app)); strncpy(info.group, el->key.group, sizeof(info.group)); - info.params = sub_app->params; // borrowed reference + info.params = sub_app->params; // 借用引用,不复制字符串 + // 添加到结果数组 utarray_push_back(groups, &info); } } @@ -132,22 +223,37 @@ UT_array *neu_subscribe_manager_get(neu_subscribe_mgr_t *mgr, const char *app) return groups; } +/** + * @brief 获取北向驱动的映射关系 + * + * 获取指定北向驱动与所有南向驱动组的映射关系 + * + * @param mgr 订阅管理器指针 + * @param ndriver 北向驱动节点名称 + * @return 包含所有映射关系的数组,即使没有找到也会返回空数组 + * @note 返回的数组需要由调用者释放 + */ UT_array *neu_subscribe_manager_get_ndriver_maps(neu_subscribe_mgr_t *mgr, - const char * ndriver) + const char *ndriver) { UT_array *groups = NULL; UT_icd icd = { sizeof(neu_resp_ndriver_map_info_t), NULL, NULL, NULL }; utarray_new(groups, &icd); sub_elem_t *el = NULL, *tmp = NULL; + // 遍历哈希表中的所有元素 HASH_ITER(hh, mgr->ss, el, tmp) { + // 检查每个元素中的应用列表 utarray_foreach(el->apps, neu_app_subscribe_t *, sub_app) { + // 如果找到匹配的北向驱动名称 if (strcmp(sub_app->app_name, ndriver) == 0) { neu_resp_ndriver_map_info_t info = { 0 }; + // 填充映射信息 strncpy(info.driver, el->key.driver, sizeof(info.driver)); strncpy(info.group, el->key.group, sizeof(info.group)); + // 添加到结果数组 utarray_push_back(groups, &info); } } @@ -156,43 +262,73 @@ UT_array *neu_subscribe_manager_get_ndriver_maps(neu_subscribe_mgr_t *mgr, return groups; } +/** + * @brief 取消指定应用的所有订阅 + * + * 取消指定应用对所有驱动和组的订阅关系 + * + * @param mgr 订阅管理器指针 + * @param app 应用节点名称 + */ void neu_subscribe_manager_unsub_all(neu_subscribe_mgr_t *mgr, const char *app) { sub_elem_t *el = NULL, *tmp = NULL; + // 遍历哈希表中的所有元素 HASH_ITER(hh, mgr->ss, el, tmp) { + // 检查每个元素中的应用列表 utarray_foreach(el->apps, neu_app_subscribe_t *, sub_app) { + // 如果找到匹配的应用名称 if (strcmp(sub_app->app_name, app) == 0) { + // 取消该应用对当前驱动和组的订阅 neu_subscribe_manager_unsub(mgr, el->key.driver, app, el->key.group); - break; + break; // 取消后跳出内层循环,继续检查下一个元素 } } } } +/** + * @brief 添加订阅关系 + * + * 添加应用对驱动组的订阅关系 + * + * @param mgr 订阅管理器指针 + * @param driver 驱动节点名称 + * @param app 应用节点名称 + * @param group 组名称 + * @param params 订阅参数,可以为NULL + * @param addr 应用节点的套接字地址 + * @return 成功返回NEU_ERR_SUCCESS,已订阅返回NEU_ERR_GROUP_ALREADY_SUBSCRIBED, + * 内存分配失败返回NEU_ERR_EINTERNAL + */ int neu_subscribe_manager_sub(neu_subscribe_mgr_t *mgr, const char *driver, const char *app, const char *group, const char *params, struct sockaddr_in addr) { - sub_elem_t * find = NULL; + sub_elem_t *find = NULL; sub_elem_key_t key = { 0 }; neu_app_subscribe_t app_sub = { 0 }; + // 构建查找键和应用订阅信息 strncpy(key.driver, driver, sizeof(key.driver)); strncpy(key.group, group, sizeof(key.group)); strncpy(app_sub.app_name, app, sizeof(app_sub.app_name)); app_sub.addr = addr; + // 如果有参数,复制参数字符串 if (params && NULL == (app_sub.params = strdup(params))) { return NEU_ERR_EINTERNAL; } + // 在哈希表中查找驱动和组 HASH_FIND(hh, mgr->ss, &key, sizeof(sub_elem_key_t), find); if (find) { + // 如果找到,检查应用是否已订阅 utarray_foreach(find->apps, neu_app_subscribe_t *, sub) { if (strcmp(sub->app_name, app) == 0) { @@ -201,24 +337,41 @@ int neu_subscribe_manager_sub(neu_subscribe_mgr_t *mgr, const char *driver, } } } else { + // 如果没有找到,创建新的订阅元素 find = calloc(1, sizeof(sub_elem_t)); utarray_new(find->apps, &app_sub_icd); find->key = key; HASH_ADD(hh, mgr->ss, key, sizeof(sub_elem_key_t), find); } + // 添加应用订阅信息到列表 utarray_push_back(find->apps, &app_sub); return NEU_ERR_SUCCESS; } +/** + * @brief 更新订阅参数 + * + * 更新应用对驱动组订阅的参数 + * + * @param mgr 订阅管理器指针 + * @param app 应用节点名称 + * @param driver 驱动节点名称 + * @param group 组名称 + * @param params 新的订阅参数,可以为NULL + * @return 成功返回NEU_ERR_SUCCESS,未订阅返回NEU_ERR_GROUP_NOT_SUBSCRIBE, + * 内存分配失败返回NEU_ERR_EINTERNAL + */ int neu_subscribe_manager_update_params(neu_subscribe_mgr_t *mgr, const char *app, const char *driver, const char *group, const char *params) { sub_elem_key_t key = { 0 }; + // 构建查找键 strncpy(key.driver, driver, sizeof(key.driver)); strncpy(key.group, group, sizeof(key.group)); + // 在哈希表中查找驱动和组 sub_elem_t *find = NULL; HASH_FIND(hh, mgr->ss, &key, sizeof(sub_elem_key_t), find); @@ -226,6 +379,7 @@ int neu_subscribe_manager_update_params(neu_subscribe_mgr_t *mgr, return NEU_ERR_GROUP_NOT_SUBSCRIBE; } + // 查找指定的应用订阅 neu_app_subscribe_t *app_sub = NULL; utarray_foreach(find->apps, neu_app_subscribe_t *, sub) { @@ -239,31 +393,48 @@ int neu_subscribe_manager_update_params(neu_subscribe_mgr_t *mgr, return NEU_ERR_GROUP_NOT_SUBSCRIBE; } + // 复制新的参数字符串 char *p = NULL; if (params && NULL == (p = strdup(params))) { return NEU_ERR_EINTERNAL; } + // 更新参数 free(app_sub->params); app_sub->params = p; return NEU_ERR_SUCCESS; } +/** + * @brief 取消订阅关系 + * + * 取消应用对驱动组的订阅关系 + * + * @param mgr 订阅管理器指针 + * @param driver 驱动节点名称 + * @param app 应用节点名称 + * @param group 组名称 + * @return 成功返回NEU_ERR_SUCCESS,未订阅返回NEU_ERR_GROUP_NOT_SUBSCRIBE + */ int neu_subscribe_manager_unsub(neu_subscribe_mgr_t *mgr, const char *driver, const char *app, const char *group) { - sub_elem_t * find = NULL; + sub_elem_t *find = NULL; sub_elem_key_t key = { 0 }; + // 构建查找键 strncpy(key.driver, driver, sizeof(key.driver)); strncpy(key.group, group, sizeof(key.group)); + // 在哈希表中查找驱动和组 HASH_FIND(hh, mgr->ss, &key, sizeof(sub_elem_key_t), find); if (find) { + // 查找指定的应用订阅 utarray_foreach(find->apps, neu_app_subscribe_t *, sub) { if (strcmp(sub->app_name, app) == 0) { + // 释放资源并从数组中删除 neu_app_subscribe_fini(sub); utarray_erase(find->apps, utarray_eltidx(find->apps, sub), 1); return NEU_ERR_SUCCESS; @@ -274,33 +445,59 @@ int neu_subscribe_manager_unsub(neu_subscribe_mgr_t *mgr, const char *driver, return NEU_ERR_GROUP_NOT_SUBSCRIBE; } +/** + * @brief 移除驱动的所有订阅关系 + * + * 移除指定驱动的所有订阅关系,如果指定了组,则只移除该组的订阅关系 + * + * @param mgr 订阅管理器指针 + * @param driver 驱动节点名称 + * @param group 组名称,如果为NULL则移除所有组 + */ void neu_subscribe_manager_remove(neu_subscribe_mgr_t *mgr, const char *driver, const char *group) { sub_elem_t *el = NULL, *tmp = NULL; + // 遍历哈希表中的所有元素 HASH_ITER(hh, mgr->ss, el, tmp) { + // 如果驱动名称匹配,且组名称匹配或组为NULL if (strcmp(driver, el->key.driver) == 0 && (group == NULL || strcmp(group, el->key.group) == 0)) { + // 从哈希表中删除 HASH_DEL(mgr->ss, el); + // 释放所有应用订阅的资源 utarray_foreach(el->apps, neu_app_subscribe_t *, sub_app) { neu_app_subscribe_fini(sub_app); } + // 释放应用数组和元素本身 utarray_free(el->apps); free(el); } } } +/** + * @brief 更新应用名称 + * + * 更新所有订阅关系中指定应用的名称 + * + * @param mgr 订阅管理器指针 + * @param app 原应用节点名称 + * @param new_name 新的应用节点名称 + * @return 始终返回0 + */ int neu_subscribe_manager_update_app_name(neu_subscribe_mgr_t *mgr, const char *app, const char *new_name) { sub_elem_t *el = NULL, *tmp = NULL; + // 遍历哈希表中的所有元素 HASH_ITER(hh, mgr->ss, el, tmp) { + // 查找并更新每个匹配的应用名称 utarray_foreach(el->apps, neu_app_subscribe_t *, sub_app) { if (strcmp(app, sub_app->app_name) == 0) { @@ -312,17 +509,32 @@ int neu_subscribe_manager_update_app_name(neu_subscribe_mgr_t *mgr, return 0; } +/** + * @brief 更新驱动名称 + * + * 更新所有订阅关系中指定驱动的名称 + * + * @param mgr 订阅管理器指针 + * @param driver 原驱动节点名称 + * @param new_name 新的驱动节点名称 + * @return 始终返回0 + */ int neu_subscribe_manager_update_driver_name(neu_subscribe_mgr_t *mgr, - const char * driver, - const char * new_name) + const char *driver, + const char *new_name) { sub_elem_t *el = NULL, *tmp = NULL; + // 遍历哈希表中的所有元素 HASH_ITER(hh, mgr->ss, el, tmp) { + // 如果驱动名称匹配 if (strcmp(driver, el->key.driver) == 0) { + // 从哈希表中删除 HASH_DEL(mgr->ss, el); + // 更新驱动名称 strncpy(el->key.driver, new_name, sizeof(el->key.driver)); + // 重新添加到哈希表 HASH_ADD(hh, mgr->ss, key, sizeof(sub_elem_key_t), el); } } @@ -330,25 +542,41 @@ int neu_subscribe_manager_update_driver_name(neu_subscribe_mgr_t *mgr, return 0; } +/** + * @brief 更新组名称 + * + * 更新指定驱动和组的名称 + * + * @param mgr 订阅管理器指针 + * @param driver 驱动节点名称 + * @param group 原组名称 + * @param new_name 新的组名称 + * @return 成功返回0,未订阅返回NEU_ERR_GROUP_NOT_SUBSCRIBE + */ int neu_subscribe_manager_update_group_name(neu_subscribe_mgr_t *mgr, - const char * driver, - const char * group, - const char * new_name) + const char *driver, + const char *group, + const char *new_name) { - sub_elem_t * find = NULL; + sub_elem_t *find = NULL; sub_elem_key_t key = { 0 }; + // 构建查找键 strncpy(key.driver, driver, sizeof(key.driver)); strncpy(key.group, group, sizeof(key.group)); + // 在哈希表中查找 HASH_FIND(hh, mgr->ss, &key, sizeof(sub_elem_key_t), find); if (NULL == find) { return NEU_ERR_GROUP_NOT_SUBSCRIBE; } + // 从哈希表中删除 HASH_DEL(mgr->ss, find); + // 更新组名称 strncpy(find->key.group, new_name, sizeof(find->key.group)); + // 重新添加到哈希表 HASH_ADD(hh, mgr->ss, key, sizeof(sub_elem_key_t), find); return 0; diff --git a/src/core/template_manager.c b/src/core/template_manager.c index 3426843ce..3a0a24c34 100644 --- a/src/core/template_manager.c +++ b/src/core/template_manager.c @@ -17,6 +17,15 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. **/ +/** + * @file template_manager.c + * @brief 模板管理器实现 + * + * 本文件实现了Neuron系统的模板管理功能,管理设备模板的创建、查找、删除等操作。 + * 模板是一种可重用的数据点配置集合,用于快速创建具有相同数据点结构的设备实例。 + * 模板管理器维护了一个基于名称的模板映射表,提供高效的模板查询和管理功能。 + */ + #include #include "base/template.h" @@ -25,112 +34,208 @@ #include "template_manager.h" +/** + * @brief 模板名称映射条目 + * + * 用于在哈希表中存储模板和相关的插件实例 + */ typedef struct { - neu_template_t * tmpl; - neu_plugin_instance_t *inst; - UT_hash_handle hh_name; + neu_template_t *tmpl; /**< 模板对象 */ + neu_plugin_instance_t *inst; /**< 关联的插件实例 */ + UT_hash_handle hh_name; /**< UT哈希句柄,用于名称索引 */ } name_entry_t; +/** + * @brief 释放插件实例资源 + * + * @param inst 插件实例指针 + */ static inline void free_plugin_instance(neu_plugin_instance_t *inst) { - dlclose(inst->handle); - free(inst); + dlclose(inst->handle); // 关闭动态链接库句柄 + free(inst); // 释放实例内存 } +/** + * @brief 释放模板条目资源 + * + * 释放模板条目及其关联的模板和插件实例资源 + * + * @param ent 模板条目指针 + */ static inline void template_entry_free(name_entry_t *ent) { - neu_template_free(ent->tmpl); - free_plugin_instance(ent->inst); - free(ent); + neu_template_free(ent->tmpl); // 释放模板 + free_plugin_instance(ent->inst); // 释放关联的插件实例 + free(ent); // 释放条目本身 } +/** + * @brief 模板管理器结构体 + * + * 管理所有模板的核心数据结构,通过哈希表实现快速查找 + */ struct neu_template_manager_s { - name_entry_t *name_map; + name_entry_t *name_map; /**< 基于名称的模板映射哈希表 */ }; +/** + * @brief 创建模板管理器 + * + * 分配并初始化一个新的模板管理器实例 + * + * @return 新创建的模板管理器指针,内存不足时可能返回NULL + */ neu_template_manager_t *neu_template_manager_create() { neu_template_manager_t *mgr = calloc(1, sizeof(*mgr)); return mgr; } +/** + * @brief 销毁模板管理器 + * + * 释放模板管理器及其管理的所有模板资源 + * + * @param mgr 要销毁的模板管理器指针 + */ void neu_template_manager_destroy(neu_template_manager_t *mgr) { if (NULL == mgr) { return; } + // 清除所有模板 neu_template_manager_clear(mgr); + // 释放管理器本身 free(mgr); } +/** + * @brief 添加模板到模板管理器 + * + * 将新创建的模板及其关联的插件实例添加到模板管理器中 + * + * @param mgr 模板管理器指针 + * @param tmpl 模板指针 + * @param inst 关联的插件实例指针 + * @return 成功返回0,模板已存在返回NEU_ERR_TEMPLATE_EXIST, + * 内存分配失败返回NEU_ERR_EINTERNAL + */ int neu_template_manager_add(neu_template_manager_t *mgr, neu_template_t *tmpl, neu_plugin_instance_t *inst) { name_entry_t *ent = NULL; - const char * name = neu_template_name(tmpl); + const char *name = neu_template_name(tmpl); + // 检查模板名称是否已存在 HASH_FIND(hh_name, mgr->name_map, name, strlen(name), ent); if (ent) { + // 如果已存在,释放资源并返回错误 neu_template_free(tmpl); free_plugin_instance(inst); return NEU_ERR_TEMPLATE_EXIST; } + // 创建新的条目 ent = calloc(1, sizeof(*ent)); if (NULL == ent) { + // 内存分配失败,释放资源并返回错误 neu_template_free(tmpl); free_plugin_instance(inst); return NEU_ERR_EINTERNAL; } + // 设置条目数据 ent->tmpl = tmpl; ent->inst = inst; + // 添加到哈希表 HASH_ADD_KEYPTR(hh_name, mgr->name_map, name, strlen(name), ent); return 0; } +/** + * @brief 从模板管理器中删除模板 + * + * 根据模板名称从模板管理器中删除模板及其关联资源 + * + * @param mgr 模板管理器指针 + * @param name 要删除的模板名称 + * @return 成功返回0,模板不存在返回NEU_ERR_TEMPLATE_NOT_FOUND + */ int neu_template_manager_del(neu_template_manager_t *mgr, const char *name) { name_entry_t *ent = NULL; + // 查找指定名称的模板 HASH_FIND(hh_name, mgr->name_map, name, strlen(name), ent); if (NULL == ent) { return NEU_ERR_TEMPLATE_NOT_FOUND; } + // 从哈希表中删除 HASH_DELETE(hh_name, mgr->name_map, ent); + // 释放条目资源 template_entry_free(ent); return 0; } +/** + * @brief 获取模板管理器中的模板数量 + * + * @param mgr 模板管理器指针 + * @return 管理器中模板的数量 + */ int neu_template_manager_count(const neu_template_manager_t *mgr) { return HASH_CNT(hh_name, mgr->name_map); } +/** + * @brief 根据名称查找模板 + * + * @param mgr 模板管理器指针 + * @param name 要查找的模板名称 + * @return 如果找到返回模板指针,否则返回NULL + */ neu_template_t *neu_template_manager_find(const neu_template_manager_t *mgr, - const char * name) + const char *name) { name_entry_t *ent = NULL; + // 在哈希表中查找指定名称的模板 HASH_FIND(hh_name, mgr->name_map, name, strlen(name), ent); return ent ? ent->tmpl : NULL; } +/** + * @brief 在指定模板中查找组 + * + * 根据模板名称和组名称查找特定的组 + * + * @param mgr 模板管理器指针 + * @param name 模板名称 + * @param group_name 组名称 + * @param group_p 如果不为NULL,成功时将设置为找到的组指针 + * @return 成功返回0,模板不存在返回NEU_ERR_TEMPLATE_NOT_FOUND, + * 组不存在返回NEU_ERR_GROUP_NOT_EXIST + */ int neu_template_manager_find_group(const neu_template_manager_t *mgr, const char *name, const char *group_name, neu_group_t **group_p) { + // 查找模板 neu_template_t *tmpl = neu_template_manager_find(mgr, name); if (NULL == tmpl) { return NEU_ERR_TEMPLATE_NOT_FOUND; } + // 在模板中查找组 neu_group_t *grp = neu_template_get_group(tmpl, group_name); if (NULL == grp) { return NEU_ERR_GROUP_NOT_EXIST; } + // 如果提供了输出参数,设置找到的组 if (group_p) { *group_p = grp; } @@ -138,6 +243,16 @@ int neu_template_manager_find_group(const neu_template_manager_t *mgr, return 0; } +/** + * @brief 遍历所有模板并执行回调函数 + * + * 对模板管理器中的每个模板执行指定的回调函数 + * + * @param mgr 模板管理器指针 + * @param cb 要执行的回调函数,接收模板指针和用户数据指针作为参数 + * @param data 传递给回调函数的用户数据指针 + * @return 如果所有回调都成功返回0,否则返回第一个非零的回调返回值 + */ int neu_template_manager_for_each(neu_template_manager_t *mgr, int (*cb)(neu_template_t *tmpl, void *data), void *data) @@ -145,22 +260,34 @@ int neu_template_manager_for_each(neu_template_manager_t *mgr, int rv = 0; name_entry_t *ent = NULL, *tmp = NULL; + // 遍历哈希表中的所有模板 HASH_ITER(hh_name, mgr->name_map, ent, tmp) { + // 对每个模板执行回调函数 if (0 != (rv = cb(ent->tmpl, data))) { - break; + break; // 如果回调返回非零值,中断遍历 } } return rv; } +/** + * @brief 清除模板管理器中的所有模板 + * + * 删除并释放模板管理器中的所有模板及其关联资源 + * + * @param mgr 模板管理器指针 + */ void neu_template_manager_clear(neu_template_manager_t *mgr) { name_entry_t *ent = NULL, *tmp = NULL; + // 遍历哈希表中的所有模板 HASH_ITER(hh_name, mgr->name_map, ent, tmp) { + // 从哈希表中删除 HASH_DELETE(hh_name, mgr->name_map, ent); + // 释放条目资源 template_entry_free(ent); } } diff --git a/src/event/event_linux.c b/src/event/event_linux.c index 70764de86..228e50aaf 100644 --- a/src/event/event_linux.c +++ b/src/event/event_linux.c @@ -16,6 +16,20 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. **/ + +/** + * @file event_linux.c + * @brief Linux平台上的事件处理实现 + * + * 该文件实现了Neuron系统在Linux平台上的事件处理机制,基于epoll实现。 + * 主要功能包括: + * 1. 事件循环创建和管理 + * 2. 定时器事件的添加和删除 + * 3. IO事件的添加和删除 + * + * 系统使用epoll监听文件描述符上的事件,并在事件触发时调用相应的回调函数。 + * 定时器事件使用timerfd实现,IO事件直接使用文件描述符。 + */ #include #include #include @@ -33,51 +47,80 @@ #include #include +/** + * @brief 定时器事件结构体 + * + * 表示一个定时器事件的上下文信息 + */ struct neu_event_timer { - int fd; - struct event_data * event_data; - struct itimerspec value; - neu_event_timer_type_e type; - pthread_mutex_t mtx; - bool stop; + int fd; /**< 定时器文件描述符 */ + struct event_data *event_data; /**< 关联的事件数据指针 */ + struct itimerspec value; /**< 定时器的时间配置 */ + neu_event_timer_type_e type; /**< 定时器类型(阻塞/非阻塞) */ + pthread_mutex_t mtx; /**< 保护定时器操作的互斥锁 */ + bool stop; /**< 定时器是否已停止 */ }; +/** + * @brief IO事件结构体 + * + * 表示一个IO事件的上下文信息 + */ struct neu_event_io { - int fd; - struct event_data *event_data; + int fd; /**< IO事件的文件描述符 */ + struct event_data *event_data; /**< 关联的事件数据指针 */ }; +/** + * @brief 事件数据结构体 + * + * 用于存储事件的详细信息,包括类型、回调函数、上下文等 + */ struct event_data { enum { - TIMER = 0, - IO = 1, + TIMER = 0, /**< 定时器事件 */ + IO = 1, /**< IO事件 */ } type; union { - neu_event_io_callback io; - neu_event_timer_callback timer; + neu_event_io_callback io; /**< IO事件回调函数 */ + neu_event_timer_callback timer; /**< 定时器事件回调函数 */ } callback; union { - neu_event_io_t io; - neu_event_timer_t timer; + neu_event_io_t io; /**< IO事件上下文 */ + neu_event_timer_t timer; /**< 定时器事件上下文 */ } ctx; - void *usr_data; - int fd; - int index; - bool use; + void *usr_data; /**< 用户数据,传递给回调函数 */ + int fd; /**< 事件关联的文件描述符 */ + int index; /**< 事件在事件数组中的索引 */ + bool use; /**< 标记该事件槽是否被使用 */ }; +/** 事件数组的最大容量 */ #define EVENT_SIZE 1400 +/** + * @brief 事件管理器结构体 + * + * 管理所有事件的核心数据结构 + */ struct neu_events { - int epoll_fd; - pthread_t thread; - bool stop; + int epoll_fd; /**< epoll实例的文件描述符 */ + pthread_t thread; /**< 事件循环线程 */ + bool stop; /**< 事件循环是否应该停止 */ - pthread_mutex_t mtx; - int n_event; - struct event_data event_datas[EVENT_SIZE]; + pthread_mutex_t mtx; /**< 保护事件数组的互斥锁 */ + int n_event; /**< 当前事件数量 */ + struct event_data event_datas[EVENT_SIZE]; /**< 事件数据数组 */ }; +/** + * @brief 获取一个空闲的事件槽位 + * + * 在事件数组中查找一个未使用的槽位,并标记为已使用 + * + * @param events 事件管理器指针 + * @return 成功返回事件索引,失败返回-1 + */ static int get_free_event(neu_events_t *events) { int ret = -1; @@ -95,6 +138,14 @@ static int get_free_event(neu_events_t *events) return ret; } +/** + * @brief 释放事件槽位 + * + * 将指定索引的事件槽位标记为未使用 + * + * @param events 事件管理器指针 + * @param index 要释放的事件索引 + */ static void free_event(neu_events_t *events, int index) { pthread_mutex_lock(&events->mtx); @@ -103,6 +154,14 @@ static void free_event(neu_events_t *events, int index) pthread_mutex_unlock(&events->mtx); } +/** + * @brief 事件循环线程函数 + * + * 持续监听epoll事件,并调用相应的回调函数处理事件 + * + * @param arg 事件管理器指针 + * @return NULL + */ static void *event_loop(void *arg) { neu_events_t *events = (neu_events_t *) arg; @@ -112,40 +171,49 @@ static void *event_loop(void *arg) struct epoll_event event = { 0 }; struct event_data *data = NULL; + // 等待事件,超时时间为1秒 int ret = epoll_wait(epoll_fd, &event, 1, 1000); if (ret == 0) { + // 超时,继续等待 continue; } if (ret == -1 && errno == EINTR) { + // 被信号中断,继续等待 continue; } if (ret == -1 || events->stop) { + // 出错或被要求停止,退出循环 zlog_warn(neuron, "event loop exit, errno: %s(%d), stop: %d", strerror(errno), errno, events->stop); break; } + // 获取事件关联的数据 data = (struct event_data *) event.data.ptr; + // 根据事件类型处理 switch (data->type) { - case TIMER: + case TIMER: // 定时器事件 pthread_mutex_lock(&data->ctx.timer.mtx); if ((event.events & EPOLLIN) == EPOLLIN) { uint64_t t; + // 读取定时器数据,清除通知 ssize_t size = read(data->fd, &t, sizeof(t)); - (void) size; + (void) size; // 忽略返回值 if (!data->ctx.timer.stop) { if (data->ctx.timer.type == NEU_EVENT_TIMER_BLOCK) { + // 阻塞型定时器:在回调执行期间从epoll中移除,执行完后再添加回来 epoll_ctl(epoll_fd, EPOLL_CTL_DEL, data->fd, NULL); ret = data->callback.timer(data->usr_data); timerfd_settime(data->fd, 0, &data->ctx.timer.value, NULL); epoll_ctl(epoll_fd, EPOLL_CTL_ADD, data->fd, &event); } else { + // 非阻塞型定时器:直接执行回调 ret = data->callback.timer(data->usr_data); } } @@ -153,19 +221,22 @@ static void *event_loop(void *arg) pthread_mutex_unlock(&data->ctx.timer.mtx); break; - case IO: + case IO: // IO事件 if ((event.events & EPOLLHUP) == EPOLLHUP) { + // 挂起事件 data->callback.io(NEU_EVENT_IO_HUP, data->fd, data->usr_data); break; } if ((event.events & EPOLLRDHUP) == EPOLLRDHUP) { + // 对端关闭连接事件 data->callback.io(NEU_EVENT_IO_CLOSED, data->fd, data->usr_data); break; } if ((event.events & EPOLLIN) == EPOLLIN) { + // 可读事件 data->callback.io(NEU_EVENT_IO_READ, data->fd, data->usr_data); break; } @@ -177,63 +248,104 @@ static void *event_loop(void *arg) return NULL; }; +/** + * @brief 创建新的事件管理器 + * + * 分配并初始化事件管理器,创建epoll实例,启动事件循环线程 + * + * @return 新创建的事件管理器指针 + */ neu_events_t *neu_event_new(void) { + // 分配事件管理器内存 neu_events_t *events = calloc(1, sizeof(struct neu_events)); + // 创建epoll实例 events->epoll_fd = epoll_create(1); nlog_notice("create epoll: %d(%d)", events->epoll_fd, errno); - assert(events->epoll_fd > 0); + assert(events->epoll_fd > 0); // 确保创建成功 + // 初始化字段 events->stop = false; events->n_event = 0; pthread_mutex_init(&events->mtx, NULL); + // 创建事件循环线程 pthread_create(&events->thread, NULL, event_loop, events); return events; }; +/** + * @brief 关闭事件管理器 + * + * 停止事件循环线程,释放事件管理器资源 + * + * @param events 要关闭的事件管理器指针 + * @return 始终返回0 + */ int neu_event_close(neu_events_t *events) { + // 设置停止标志 events->stop = true; + // 关闭epoll实例 close(events->epoll_fd); + // 等待事件循环线程结束 pthread_join(events->thread, NULL); + // 销毁互斥锁 pthread_mutex_destroy(&events->mtx); + // 释放事件管理器内存 free(events); return 0; } -neu_event_timer_t *neu_event_add_timer(neu_events_t * events, +/** + * @brief 添加定时器事件 + * + * 创建一个定时器事件并添加到事件管理器中 + * + * @param events 事件管理器指针 + * @param timer 定时器参数 + * @return 新创建的定时器事件指针 + */ +neu_event_timer_t *neu_event_add_timer(neu_events_t *events, neu_event_timer_param_t timer) { - int ret = 0; - int timer_fd = timerfd_create(CLOCK_MONOTONIC, 0); - struct itimerspec value = { + int ret = 0; + // 创建timerfd + int timer_fd = timerfd_create(CLOCK_MONOTONIC, 0); + // 设置定时器参数 + struct itimerspec value = { .it_value.tv_sec = timer.second, .it_value.tv_nsec = timer.millisecond * 1000 * 1000, .it_interval.tv_sec = timer.second, .it_interval.tv_nsec = timer.millisecond * 1000 * 1000, }; + + // 获取空闲事件槽位 int index = get_free_event(events); if (index < 0) { zlog_notice(neuron, "no free event: %d", events->epoll_fd); } assert(index >= 0); + // 获取定时器上下文 neu_event_timer_t *timer_ctx = &events->event_datas[index].ctx.timer; timer_ctx->event_data = &events->event_datas[index]; + // 设置epoll事件 struct epoll_event event = { .events = EPOLLIN, .data.ptr = timer_ctx->event_data, }; + // 启动定时器 timerfd_settime(timer_fd, 0, &value, NULL); + // 设置事件数据 timer_ctx->event_data->type = TIMER; timer_ctx->event_data->fd = timer_fd; timer_ctx->event_data->usr_data = timer.usr_data; @@ -241,12 +353,14 @@ neu_event_timer_t *neu_event_add_timer(neu_events_t * events, timer_ctx->event_data->ctx.timer = events->event_datas[index].ctx.timer; timer_ctx->event_data->index = index; + // 设置定时器上下文 timer_ctx->value = value; timer_ctx->fd = timer_fd; timer_ctx->type = timer.type; timer_ctx->stop = false; pthread_mutex_init(&timer_ctx->mtx, NULL); + // 添加到epoll ret = epoll_ctl(events->epoll_fd, EPOLL_CTL_ADD, timer_fd, &event); zlog_notice(neuron, @@ -259,39 +373,67 @@ neu_event_timer_t *neu_event_add_timer(neu_events_t * events, return timer_ctx; } +/** + * @brief 删除定时器事件 + * + * 停止并删除指定的定时器事件,释放相关资源 + * + * @param events 事件管理器指针 + * @param timer 要删除的定时器事件指针 + * @return 始终返回0 + */ int neu_event_del_timer(neu_events_t *events, neu_event_timer_t *timer) { zlog_notice(neuron, "del timer: %d from epoll: %d, index: %d", timer->fd, events->epoll_fd, timer->event_data->index); + // 设置停止标志 timer->stop = true; + // 从epoll中删除 epoll_ctl(events->epoll_fd, EPOLL_CTL_DEL, timer->fd, NULL); + // 安全关闭文件描述符 pthread_mutex_lock(&timer->mtx); close(timer->fd); pthread_mutex_unlock(&timer->mtx); + // 销毁互斥锁 pthread_mutex_destroy(&timer->mtx); + // 释放事件槽位 free_event(events, timer->event_data->index); return 0; } +/** + * @brief 添加IO事件 + * + * 将指定的文件描述符添加到事件管理器中监听IO事件 + * + * @param events 事件管理器指针 + * @param io IO事件参数 + * @return 新创建的IO事件指针 + */ neu_event_io_t *neu_event_add_io(neu_events_t *events, neu_event_io_param_t io) { - int ret = 0; + int ret = 0; + // 获取空闲事件槽位 int index = get_free_event(events); nlog_notice("add io, fd: %d, epoll: %d, index: %d", io.fd, events->epoll_fd, index); assert(index >= 0); - neu_event_io_t *io_ctx = &events->event_datas[index].ctx.io; - io_ctx->event_data = &events->event_datas[index]; + // 获取IO上下文 + neu_event_io_t *io_ctx = &events->event_datas[index].ctx.io; + io_ctx->event_data = &events->event_datas[index]; + + // 设置epoll事件,监听读取、错误、挂起和对端关闭事件 struct epoll_event event = { .events = EPOLLIN | EPOLLERR | EPOLLHUP | EPOLLRDHUP, .data.ptr = io_ctx->event_data, }; + // 设置事件数据 io_ctx->event_data->type = IO; io_ctx->event_data->fd = io.fd; io_ctx->event_data->usr_data = io.usr_data; @@ -299,27 +441,40 @@ neu_event_io_t *neu_event_add_io(neu_events_t *events, neu_event_io_param_t io) io_ctx->event_data->ctx.io = events->event_datas[index].ctx.io; io_ctx->event_data->index = index; + // 设置IO上下文 io_ctx->fd = io.fd; + // 添加到epoll ret = epoll_ctl(events->epoll_fd, EPOLL_CTL_ADD, io.fd, &event); nlog_notice("add io, fd: %d, epoll: %d, ret: %d(%d), index: %d", io.fd, events->epoll_fd, ret, errno, index); - assert(ret == 0); + assert(ret == 0); // 确保添加成功 return io_ctx; } +/** + * @brief 删除IO事件 + * + * 从事件管理器中删除指定的IO事件,停止监听该文件描述符 + * + * @param events 事件管理器指针 + * @param io 要删除的IO事件指针 + * @return 始终返回0 + */ int neu_event_del_io(neu_events_t *events, neu_event_io_t *io) { if (io == NULL) { - return 0; + return 0; // 空指针直接返回 } zlog_notice(neuron, "del io: %d from epoll: %d, index: %d", io->fd, events->epoll_fd, io->event_data->index); + // 从epoll中删除 epoll_ctl(events->epoll_fd, EPOLL_CTL_DEL, io->fd, NULL); + // 释放事件槽位 free_event(events, io->event_data->index); return 0;