文章目錄
- 前言
- 一、系統設計要點
- 二、核心數據結構
- 2.1 設備唯一標識(DeviceUID)
- 2.2 節點信息(Node)
- 2.3 節點管理器(NodeManager)
- 三、核心算法實現
- 3.1 初始化與清理
- 3.1.1 初始化節點管理器
- 3.1.2 清理節點管理器
- 3.2 動態ID分配策略
- 3.2.1 查找最小可用ID
- 3.2.2 ID使用檢查
- 3.3 心跳處理機制
- 3.4 超時檢測機制
- 四、節點查找與管理
- 4.1 通過UID查找節點
- 4.2 通過ID查找節點
- 4.3 獲取活躍節點列表
- 五、回調機制實現
- 5.1 回調函數注冊
- 5.2 示例回調函數
- 六、應用示例
- 七、總結
前言
在嵌入式設備網絡中,節點的動態加入與退出是常態,尤其是在無人機、傳感器網絡、智能家居等系統中,節點通常無法提前預設 ID,這就要求系統具備動態 ID 分配與管理能力。本篇博客將圍繞“動態 ID 管理”這一核心,介紹一個基于設備 UID 的動態 ID 分配系統,支持重復上線檢測、最小可用 ID 分配、ID 沖突檢測、ID 釋放與復用等功能,代碼完全由 C 語言實現,結構清晰,易于移植與擴展。
一、系統設計要點
該系統以節點唯一標識符 DeviceUID 為基礎,實現了以下關鍵特性:
-
? 動態 ID 分配:無需提前為設備分配 ID,系統自動為新設備分配最小可用 ID。
-
? UID 唯一識別機制:通過對 UID 的比較實現節點重復檢測與狀態更新。
-
? ID 沖突檢測:避免多個設備使用相同 ID 導致狀態混亂。
-
? ID 釋放與復用:支持節點主動釋放 ID,或超時后自動回收,以復用資源。
-
? 回調機制:支持注冊上線、下線回調函數,便于系統業務集成。
-
? 心跳檢測:通過心跳機制維護節點活躍狀態
-
? 超時處理:自動檢測并清理離線節點
二、核心數據結構
2.1 設備唯一標識(DeviceUID)
typedef struct {uint8_t bytes[6]; // 6字節的唯一設備標識
} DeviceUID;
這個結構體用于存儲設備的唯一標識符,通常可以是MAC地址或其他硬件唯一ID。
2.2 節點信息(Node)
typedef struct Node {uint8_t id; // 分配的節點IDDeviceUID uid; // 設備唯一標識uint64_t lastSeenMs; // 最后活躍時間戳(毫秒)struct Node* next; // 下一個節點指針
} Node;
每個節點包含分配的ID、設備唯一標識、最后活躍時間和指向下一個節點的指針。
2.3 節點管理器(NodeManager)
typedef struct {Node* head; // 鏈表頭指針uint8_t activeCount; // 活躍節點計數NodeOnlineCallback onOnline; // 節點上線回調函數NodeOfflineCallback onOffline; // 節點下線回調函數
} NodeManager;
節點管理器維護所有活躍節點的鏈表,并提供回調函數接口。
三、核心算法實現
3.1 初始化與清理
3.1.1 初始化節點管理器
void NodeManager_Init(NodeManager* manager) {manager->head = NULL;manager->activeCount = 0;manager->onOnline = NULL;manager->onOffline = NULL;
}
3.1.2 清理節點管理器
void NodeManager_Cleanup(NodeManager* manager) {Node* current = manager->head;while (current) {Node* next = current->next;// 回調通知節點離線if (manager->onOffline) {manager->onOffline(current->id, ¤t->uid);}free(current);current = next;}manager->head = NULL;manager->activeCount = 0;
}
3.2 動態ID分配策略
3.2.1 查找最小可用ID
static uint8_t FindMinAvailableID(NodeManager* manager) {for (uint8_t id = MIN_VALID_ID; id <= MAX_VALID_ID; id++) {if (!IsIDUsed(manager, id)) return id;}return INVALID_ID;
}
該算法從MIN_VALID_ID開始遍歷,返回第一個未被使用的ID。
3.2.2 ID使用檢查
static bool IsIDUsed(NodeManager* manager, uint8_t id) {Node* current = manager->head;while (current) {if (current->id == id) return true;current = current->next;}return false;
}
3.3 心跳處理機制
uint8_t ProcessHeartbeat(NodeManager* manager, uint8_t nodeId, const DeviceUID* uid) {// 1. 檢查是否已有相同UID的節點Node* existing = FindNodeByUID(manager, uid);if (existing) {existing->lastSeenMs = GetSysTimeMs(); // 更新活躍時間return existing->id;}// 2. 檢查請求的ID是否已被占用if (nodeId != INVALID_ID && FindNodeByID(manager, nodeId)) {nodeId = INVALID_ID; // 如果已被占用,則重置為無效ID}// 3. 分配新IDif (nodeId == INVALID_ID) {nodeId = FindMinAvailableID(manager);if (nodeId == INVALID_ID) return INVALID_ID; // 無可用ID}// 4. 添加新節點return AddNode(manager, nodeId, uid) ? nodeId : INVALID_ID;
}
-
心跳處理流程:
-
如果是已知節點,更新其活躍時間
-
如果是新節點,檢查請求ID是否可用
-
分配最小可用ID
-
添加新節點到管理器
-
3.4 超時檢測機制
void CheckTimeoutNodes(NodeManager* manager) {uint64_t now = GetSysTimeMs();Node** pnode = &manager->head;while (*pnode) {Node* current = *pnode;if ((now - current->lastSeenMs) > HEARTBEAT_TIMEOUT) {*pnode = current->next; // 從鏈表中移除// 回調通知節點離線if (manager->onOffline) {manager->onOffline(current->id, ¤t->uid);}free(current); // 釋放節點內存manager->activeCount--;} else {pnode = &(*pnode)->next;}}
}
該函數遍歷所有節點,檢查最后活躍時間是否超時,超時則移除節點并觸發下線回調。
四、節點查找與管理
4.1 通過UID查找節點
static Node* FindNodeByUID(NodeManager* manager, const DeviceUID* uid) {Node* current = manager->head;while (current) {if (CompareDeviceUID(¤t->uid, uid)) return current;current = current->next;}return NULL;
}
4.2 通過ID查找節點
static Node* FindNodeByID(NodeManager* manager, uint8_t id) {Node* current = manager->head;while (current) {if (current->id == id) return current;current = current->next;}return NULL;
}
4.3 獲取活躍節點列表
uint8_t GetActiveNodeIDs(NodeManager* manager, uint8_t* outputBuffer, uint8_t bufferSize) {uint8_t count = 0;Node* current = manager->head;while (current && count < bufferSize) {outputBuffer[count++] = current->id;current = current->next;}qsort(outputBuffer, count, sizeof(uint8_t), CompareNodeIDs);return count;
}
五、回調機制實現
5.1 回調函數注冊
void NodeManager_RegisterCallbacks(NodeManager* manager, NodeOnlineCallback onOnline, NodeOfflineCallback onOffline) {manager->onOnline = onOnline;manager->onOffline = onOffline;
}
5.2 示例回調函數
void OnNodeOnline(uint8_t id, const DeviceUID* uid) {printf("[Callback] Node %d is ONLINE!\n", id);
}void OnNodeOffline(uint8_t id, const DeviceUID* uid) {printf("[Callback] Node %d is OFFLINE!\n", id);
}
六、應用示例
void TestNodeManager() {NodeManager manager;NodeManager_Init(&manager);// 注冊回調NodeManager_RegisterCallbacks(&manager, OnNodeOnline, OnNodeOffline);// 模擬設備UIDDeviceUID uid1 = {{0x00, 0x11, 0x22, 0x33, 0x44, 0x55}};DeviceUID uid2 = {{0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB}};// 節點上線ProcessHeartbeat(&manager, 1, &uid1); // 指定ID=1ProcessHeartbeat(&manager, INVALID_ID, &uid2); // 自動分配ID// 打印活躍節點PrintActiveNodes(&manager);// 模擬超時printf("\nSimulating timeout...\n");GetSysTimeMs(); // 模擬時間流逝CheckTimeoutNodes(&manager);PrintActiveNodes(&manager);// 主動釋放節點printf("\nManually releasing node...\n");ReleaseNodeID(&manager, 2);PrintActiveNodes(&manager);// 清理NodeManager_Cleanup(&manager);
}
七、總結
本文詳細介紹了一個高效的動態ID管理系統的設計與實現,該系統具有以下優點:
-
靈活性:支持動態ID分配和釋放
-
可靠性:通過心跳機制確保節點狀態準確
-
可擴展性:易于添加新功能如安全驗證等
-
低開銷:內存占用小,適合嵌入式環境
-
事件驅動:通過回調機制實現松耦合
這種動態ID管理方案非常適合物聯網設備、傳感器網絡等需要管理大量動態節點的嵌入式應用場景。