数据云推送的代码设计

8 阅读8分钟

/**

  • @file osd_task.c
  • @brief 水阀 OSD (On Screen Display) 任务实现
  • @details 负责水阀状态的周期性采集、缓存和上报
  • 设计方案:
    1. 功能概述
    • 周期性轮询所有绑定的水阀节点
    • 发送状态请求并接收响应
    • 缓存水阀状态数据
    • 定期推送 OSD 数据到云端
    1. 设计架构
    • 状态管理:使用 valve_osd_task_state_t 结构体统一管理任务状态
    • 功能模块化:将核心功能拆分为独立函数
    • 事件驱动:通过回调函数处理响应
    • 错误处理:包含超时重试机制
    1. 核心组件
    • valve_osd_task:主任务函数,协调各功能执行
    • request_node_state:发送状态请求并等待响应
    • push_node_data:推送单个节点的 OSD 数据
    • push_all_nodes_data:推送所有节点的 OSD 数据
    • req_state_pack_cb_func:状态响应回调函数
    1. 工作流程
    • 初始化:创建信号量,初始化状态
    • 轮询:每 500ms 轮询一个节点
    • 请求:发送状态请求,最多重试 3 次
    • 响应:通过回调函数处理响应数据
    • 推送:每 30 秒推送所有节点数据
    1. 关键技术点
    • 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 周期性执行以下操作:

    1. 轮询所有绑定的阀门节点
    1. 发送状态请求并等待响应
    1. 缓存阀门状态数据
    1. 定期推送 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; } } }