/**
- @file osd_task.c
- @brief 水阀 OSD (On Screen Display) 任务实现
- @details 负责水阀状态的周期性采集、缓存和上报
- 设计方案:
-
- 功能概述
-
- 周期性轮询所有绑定的水阀节点
-
- 发送状态请求并接收响应
-
- 缓存水阀状态数据
-
- 定期推送 OSD 数据到云端
-
- 设计架构
-
- 状态管理:使用 valve_osd_task_state_t 结构体统一管理任务状态
-
- 功能模块化:将核心功能拆分为独立函数
-
- 事件驱动:通过回调函数处理响应
-
- 错误处理:包含超时重试机制
-
- 核心组件
-
- valve_osd_task:主任务函数,协调各功能执行
-
- request_node_state:发送状态请求并等待响应
-
- push_node_data:推送单个节点的 OSD 数据
-
- push_all_nodes_data:推送所有节点的 OSD 数据
-
- req_state_pack_cb_func:状态响应回调函数
-
- 工作流程
-
- 初始化:创建信号量,初始化状态
-
- 轮询:每 500ms 轮询一个节点
-
- 请求:发送状态请求,最多重试 3 次
-
- 响应:通过回调函数处理响应数据
-
- 推送:每 30 秒推送所有节点数据
-
- 关键技术点
-
- LoRa 通信:使用 LoRa 协议与水阀节点通信
-
- 信号量同步:确保请求和响应的同步
-
- 缓存管理:使用数组缓存节点状态
-
- 协议缓冲区:使用 Protocol Buffers 序列化数据
-
- 错误处理:完善的超时和重试机制 */
#include <stdint.h> #include <string.h> #include "osal.h" // 操作系统抽象层 #include "lora.h" // LoRa 通信接口 #include "pb_encode.h" // Protocol Buffers 编码 #include "pb_decode.h" // Protocol Buffers 解码 #include "lora_yj_message.pb.h" // 消息定义 #include "lora_usr_cmd.h" // LoRa 用户命令 #include "valve_mgr.h" // 阀门管理 #include "product.h" // 产品信息 #include "valve_osd.h" // 阀门 OSD 定义 #include "osd_push.h" // OSD 推送
/**
- @def VALVE_OSD_STEP_INTERVAL_MS
- @brief 水阀 OSD 每节点轮询间隔(毫秒) */ #define VALVE_OSD_STEP_INTERVAL_MS (500) // 水阀 OSD 每节点轮询间隔:500 ms
/**
- @def VALVE_OSD_PUSH_CYCLE
- @brief 水阀 OSD 推送周期(轮询次数) */ #define VALVE_OSD_PUSH_CYCLE (30 * 1000 / VALVE_OSD_STEP_INTERVAL_MS) // 水阀 OSD 推送周期:30 秒
/**
- @def VALVE_REQUEST_TIMEOUT_MS
- @brief 请求最大超时时长(毫秒) */ #define VALVE_REQUEST_TIMEOUT_MS (5000) // 请求最大超时时长:5000ms
/**
- @def VALVE_REQUEST_MAX_RETRY
- @brief 每次请求最大重试次数 */ #define VALVE_REQUEST_MAX_RETRY (3) // 每次重试次数:3次
/**
- @def OSD_PUSH_MAX_RETRY
- @brief OSD 推送最大重试次数 */ #define OSD_PUSH_MAX_RETRY (3) // 最大重试次数
/**
- @def OSD_PUSH_RETRY_INTERVAL_MS
- @brief 推送重试间隔(毫秒) */ #define OSD_PUSH_RETRY_INTERVAL_MS (1000) // 推送重试间隔
/**
- @def VALVE_OSD_MAX_NODE_INDEX
- @brief 最大节点数 */ #define VALVE_OSD_MAX_NODE_INDEX (10) // 最大节点数
/**
- @var valve_osd_sem
- @brief 用于同步请求和响应的信号量 */ static osal_sem_t valve_osd_sem;
/**
- @var valve_osd_resp_src_addr
- @brief 响应消息的源地址 */ static uint16_t valve_osd_resp_src_addr;
/**
- @var lora_connect_status
- @brief LoRa 连接状态 */ static bool lora_connect_status;
/**
- @var g_valve_osd_cache
- @brief 阀门状态缓存数组 */ static valve_osd_data_t g_valve_osd_cache[VALVE_OSD_MAX_NODE_INDEX];
/**
- @var g_osd_push_dev_type
- @brief OSD 推送设备类型 */ static uint8_t g_osd_push_dev_type = DEV_TYPE_VALVE;
/**
- @brief 水阀 OSD 任务状态结构体
- @details 用于管理任务执行过程中的状态变量 */ typedef struct { uint16_t which_num; // 当前轮询的节点索引 uint16_t node_id; // 节点 ID uint32_t total; // 总节点数 int32_t push_ret; // 推送结果 int32_t get_resp; // 响应结果 uint8_t poll_count; // 轮询计数器 uint8_t found_count; // 找到的节点数 } valve_osd_task_state_t;
/**
-
@brief 推送阀门 OSD 数据
-
@param node_id 节点 ID
-
@param state_data 阀门状态数据
-
@return 0 成功,-1 失败 */ static int32_t valve_osd_push(uint16_t node_id, valve_osd_data_t *state_data) { uint8_t sn_str[64] = {0}; // 产品序列号 uint16_t sn_str_len = 0; // 序列号长度 lora_yj_Message_t message = lora_yj_Message_init_zero; // 消息结构体
// 获取产品序列号 get_product_sn_data(sn_str); sn_str_len = strlen((const char *)sn_str);
DEBUG_INFO("[valve_osd] push message, len:%d\r\n", sn_str_len);
// 序列号为空,推送失败 if (sn_str_len == 0) { return -1; }
// 填充消息数据 message.timestamp = (uint64_t)(os_get_time() / 1000); // 时间戳 message.which_payload = lora_yj_Message_osd_tag; // OSD 消息类型 message.payload.osd.which_payload = lora_yj_OSDMessage_valve_property_tag; // 阀门属性消息 message.payload.osd.payload.valve_property.lora_data.node_id = node_id; // 节点 ID message.payload.osd.payload.valve_property.lora_data.lora_connect_status = lora_connect_status; // 连接状态 message.payload.osd.payload.valve_property.lora_data.valve_err = state_data->valve_err; // 阀门错误状态 message.payload.osd.payload.valve_property.lora_data.valve_config = state_data->valve_config; // 阀门配置 message.payload.osd.payload.valve_property.lora_data.valve_opening = state_data->vh_open_percent[0]; // 阀门开度 0 message.payload.osd.payload.valve_property.lora_data.valve_opening1 = state_data->vh_open_percent[1]; // 阀门开度 1 message.payload.osd.payload.valve_property.lora_data.valve_opening2 = state_data->vh_open_percent[2]; // 阀门开度 2 message.payload.osd.payload.valve_property.lora_data.valve_opening3 = state_data->vh_open_percent[3]; // 阀门开度 3 message.payload.osd.payload.valve_property.lora_data.bat_voltage0 = state_data->bat_voltage[0]; // 电池电压 0 message.payload.osd.payload.valve_property.lora_data.bat_voltage1 = state_data->bat_voltage[1]; // 电池电压 1 message.payload.osd.payload.valve_property.lora_data.work_time = state_data->work_time; // 工作时间
// 构建推送节点 pb_node_t pb_node = { .dev_type = node_id, .packet_type = PACKET_TYPE_OSD, .param = NULL, .pb_message = NULL, };
// 发送数据包 return pb_send_packet(&pb_node, PACKET_TYPE_OSD, (const char *)sn_str, message.timestamp, &message); }
/**
-
@brief 状态请求响应回调函数
-
@param pack LoRa 用户数据包
-
@return 0 成功,-1 失败 */ static int32_t req_state_pack_cb_func(const lora_usr_pack_t *pack) { // 数据包无效,返回失败 if (!pack || !pack->data) { DEBUG_ERROR("[OSD] Invalid pack or data\r\n"); return -1; }
// 解析响应数据 ack_usr_valve_req_state_cmd_t *ack_req_state_data = (ack_usr_valve_req_state_cmd_t *)pack->data;
// 响应码非零,返回失败 if (ack_req_state_data->ret_code != 0) { return -1; }
// 唤醒等待的信号量 osal_sem_up(valve_osd_sem);
// 提取节点 ID 并更新缓存 uint16_t node_id = LORA_GET_NODE_ID(pack->src_addr); g_valve_osd_cache[node_id].node_id = node_id; memcpy(&g_valve_osd_cache[node_id].data, ack_req_state_data, sizeof(ack_usr_valve_req_state_cmd_t));
return 0; }
/**
-
@brief 发送阀门状态请求
-
@param node_id 节点 ID */ static void valve_osd_request(uint16_t node_id) { uint16_t valve_net_id = 0; // 网络 ID
// 获取本地网络 ID valve_net_id = lora_get_network_id(get_local_add_sync());
// 构建 LoRa 用户数据包 lora_usr_pack_t lora_pack = {0}; lora_pack.cmd_id = LORA_USER_CMD_REQ_VALVE_ST; // 命令 ID:请求阀门状态 lora_pack.dst_addr = lora_gen_addr(valve_net_id, node_id); // 目标地址 lora_pack.data_len = sizeof(user_valve_req_state_cmd_t); // 数据长度
// 填充请求数据 user_valve_req_state_cmd_t *req_state_data = (user_valve_req_state_cmd_t *)lora_pack.data; req_state_data->net_id = valve_net_id; // 网络 ID req_state_data->node_id = node_id; // 节点 ID req_state_data->sleep_time = 0; // 睡眠时间
// 注册响应回调函数 register_destinate_usr_cmd_cb(LORA_USER_CMD_REQ_VALVE_ST, req_state_pack_cb_func);
// 发送数据包 lora_send_usr_pack((const lora_usr_pack_t *)&lora_pack); }
/**
-
@brief 发送状态请求并等待响应
-
@param node_id 节点 ID
-
@return 0 成功,-1 失败 */ static int32_t request_node_state(uint16_t node_id) { int32_t get_resp = -1; for (uint8_t i = 0; i < VALVE_REQUEST_MAX_RETRY; i++) { osal_sem_tryrdon(valve_osd_sem); valve_osd_request(node_id);
if (osal_sem_down_timeout(valve_osd_sem, VALVE_REQUEST_TIMEOUT_MS) == OSAL_SUCCESS) { get_resp = 0; g_valve_osd_cache[node_id].lora_connect_status = true; DEBUG_INFO("[valve_osd] node %d callback received, src_addr=%04X\r\n", node_id, valve_osd_resp_src_addr); break; } else { g_valve_osd_cache[node_id].lora_connect_status = false; DEBUG_INFO("[valve] node %d request timeout, retry %d/%d\r\n", node_id, i + 1, VALVE_REQUEST_MAX_RETRY); }} return get_resp; }
/**
- @brief 推送单个节点的 OSD 数据
- @param node_id 节点 ID
- @param data 阀门状态数据 */ static void push_node_data(uint16_t node_id, valve_osd_data_t *data) { for (uint8_t j = 0; j < OSD_PUSH_MAX_RETRY; j++) { int32_t push_ret = valve_osd_push(node_id, data); if (push_ret == 0) { DEBUG_INFO("[valve_osd] node %d push success\r\n", node_id); break; } DEBUG_INFO("[valve_osd] node %d push failed, retry %d/%d\r\n", node_id, j + 1, OSD_PUSH_MAX_RETRY); osal_sleep(OSD_PUSH_RETRY_INTERVAL_MS); } }
/**
- @brief 推送所有节点的 OSD 数据 */ static void push_all_nodes_data(void) { DEBUG_INFO("[valve_osd] start push OSD data\r\n"); for (uint8_t i = 0; i < VALVE_OSD_MAX_NODE_INDEX; i++) { if (g_valve_osd_cache[i].node_id != 0) { push_node_data(g_valve_osd_cache[i].node_id, &g_valve_osd_cache[i].data); } } DEBUG_INFO("[valve_osd] push OSD data completed\r\n"); }
/**
- @brief 水阀 OSD 周期推送函数
- @details 预留函数,用于 685 周期推送 */ void valve_osd_periodic(void) // ltz 685 周期推送 { static uint16_t which_num = 0xFFFF; }
/**
-
@brief 水阀 OSD 任务
-
@details 周期性执行以下操作:
-
- 轮询所有绑定的阀门节点
-
- 发送状态请求并等待响应
-
- 缓存阀门状态数据
-
- 定期推送 OSD 数据 */ void valve_osd_task(void) { static valve_osd_task_state_t state = { .which_num = 0, .node_id = 0, .total = 0, .push_ret = -1, .get_resp = -1, .poll_count = 0, .found_count = 0 };
// 初始化信号量 if (osal_sem_init(valve_osd_sem, 0) != OSAL_SUCCESS) { DEBUG_ERROR("[valve_osd] sem init failed\r\n"); return; }
// 主循环 while (1) { // 等待轮询间隔 osal_sleep(VALVE_OSD_STEP_INTERVAL_MS); state.poll_count++;
// 获取绑定的节点总数 state.total = valve_mgr_get_count(); // 无绑定节点,继续等待 if (state.total == 0) { state.found_count = 0; DEBUG_INFO("[valve_osd] no device bound, waiting...\r\n"); continue; } // 查找当前索引对应的节点 if (DOT_SUCCESS != valve_mgr_find_by_which(state.which_num, &state.node_id)) { goto next; } state.found_count++; DEBUG_INFO("[valve_osd] index=%d found node_id=%d, found_count=%d/total=%d\r\n", state.which_num, state.node_id, state.found_count, state.total); // 发送状态请求并等待响应 state.get_resp = request_node_state(state.node_id); // 达到推送周期,推送 OSD 数据 if (state.poll_count >= VALVE_OSD_PUSH_CYCLE) { push_all_nodes_data(); state.poll_count = 0; // 重置轮询计数器 } // 未收到响应,继续下一个节点 if (state.get_resp != 0) { goto next; }next: // 移动到下一个节点 state.which_num++; // 遍历完所有节点或找到所有绑定节点,重置索引 if (state.which_num >= VALVE_OSD_MAX_NODE_INDEX || state.found_count >= state.total) { state.which_num = 0; state.found_count = 0; } } }