/** * 自然写教室智能网关管理软件 V1.0 * * device_manager.c - 设备发现与管理模块 * * 功能说明: * - BLE设备自动扫描与发现 * - 安全配对管理(Numeric Comparison模式) * - 设备信息数据库(SQLite持久化) * - 设备在线状态跟踪与心跳超时检测 * - 设备电量监控与低电量告警 * - 最大支持40+支笔同时在线 */ #include #include #include #include #include #include #include #include /* ======================== 常量定义 ======================== */ /* 最大设备数量 */ #define MAX_DEVICES 64 /* 心跳超时时间(秒)- 超过此时间未收到心跳视为离线 */ #define HEARTBEAT_TIMEOUT_SEC 30 /* 低电量告警阈值(百分比) */ #define LOW_BATTERY_THRESHOLD 10 /* 设备信息数据库路径 */ #define DEVICE_DB_PATH "/var/lib/writech/devices.db" /* 设备名称最大长度 */ #define DEVICE_NAME_MAX 64 /* 设备列表检查间隔(秒) */ #define DEVICE_CHECK_INTERVAL 5 /* ======================== 数据结构 ======================== */ /* 设备类型 */ typedef enum { DEVICE_TYPE_PEN = 0x01, /* 智能点阵笔 */ DEVICE_TYPE_CHARGER = 0x02, /* 充电底座 */ DEVICE_TYPE_UNKNOWN = 0xFF /* 未知设备 */ } device_type_t; /* 设备连接状态 */ typedef enum { DEVICE_STATE_DISCONNECTED = 0, /* 已断开 */ DEVICE_STATE_CONNECTING = 1, /* 连接中 */ DEVICE_STATE_PAIRED = 2, /* 已配对未连接 */ DEVICE_STATE_CONNECTED = 3, /* 已连接 */ DEVICE_STATE_ACTIVE = 4 /* 活跃(正在书写) */ } device_state_t; /* 设备信息结构 */ typedef struct { uint8_t mac_addr[6]; /* BLE MAC地址 */ char name[DEVICE_NAME_MAX]; /* 设备名称 */ device_type_t type; /* 设备类型 */ device_state_t state; /* 连接状态 */ uint8_t battery_level; /* 电量百分比(0-100) */ int8_t rssi; /* 信号强度(dBm) */ uint16_t firmware_version; /* 固件版本号 */ time_t first_seen; /* 首次发现时间 */ time_t last_heartbeat; /* 最后心跳时间 */ time_t last_data_time; /* 最后数据接收时间 */ uint32_t total_strokes; /* 累计笔迹数据量 */ uint32_t reconnect_count; /* 重连次数 */ bool low_battery_notified; /* 是否已发送低电量通知 */ bool paired; /* 是否已配对 */ uint8_t slot_index; /* 在连接表中的槽位 */ } device_info_t; /* 设备管理器 */ typedef struct { device_info_t devices[MAX_DEVICES]; /* 设备列表 */ int device_count; /* 当前设备数量 */ pthread_mutex_t mutex; /* 线程安全锁 */ pthread_t monitor_thread; /* 状态监控线程 */ bool running; /* 运行标志 */ bool scanning; /* 是否正在扫描 */ uint32_t total_connected; /* 当前在线设备数 */ uint32_t total_disconnects; /* 累计断连次数 */ char gateway_id[32]; /* 所属网关ID */ } device_manager_t; /* 全局设备管理器实例 */ static device_manager_t g_dev_mgr; /* ======================== 内部工具函数 ======================== */ /** * MAC地址比较 */ static bool mac_equals(const uint8_t a[6], const uint8_t b[6]) { return memcmp(a, b, 6) == 0; } /** * MAC地址转字符串 */ static void mac_to_str(const uint8_t mac[6], char *buf, int buf_len) { snprintf(buf, buf_len, "%02X:%02X:%02X:%02X:%02X:%02X", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); } /** * 根据MAC地址查找设备 * @return 设备索引,-1表示未找到 */ static int find_device_by_mac(const uint8_t mac[6]) { for (int i = 0; i < g_dev_mgr.device_count; i++) { if (mac_equals(g_dev_mgr.devices[i].mac_addr, mac)) { return i; } } return -1; } /** * 查找空闲的设备槽位 */ static int find_free_slot(void) { if (g_dev_mgr.device_count >= MAX_DEVICES) { return -1; } return g_dev_mgr.device_count; } /** * 统计当前在线设备数 */ static uint32_t count_online_devices(void) { uint32_t count = 0; for (int i = 0; i < g_dev_mgr.device_count; i++) { if (g_dev_mgr.devices[i].state >= DEVICE_STATE_CONNECTED) { count++; } } return count; } /** * 检查设备心跳超时 * 将超时设备标记为断开状态 */ static void check_heartbeat_timeout(void) { time_t now = time(NULL); for (int i = 0; i < g_dev_mgr.device_count; i++) { device_info_t *dev = &g_dev_mgr.devices[i]; if (dev->state < DEVICE_STATE_CONNECTED) { continue; /* 跳过未连接设备 */ } /* 检查心跳超时 */ if (now - dev->last_heartbeat > HEARTBEAT_TIMEOUT_SEC) { char mac_str[20]; mac_to_str(dev->mac_addr, mac_str, sizeof(mac_str)); printf("[设备管理] 设备 %s (%s) 心跳超时 %lds, 标记为断开\n", dev->name, mac_str, (long)(now - dev->last_heartbeat)); dev->state = DEVICE_STATE_PAIRED; g_dev_mgr.total_disconnects++; } } /* 更新在线设备计数 */ g_dev_mgr.total_connected = count_online_devices(); } /** * 检查低电量设备并发送告警 */ static void check_low_battery(void) { for (int i = 0; i < g_dev_mgr.device_count; i++) { device_info_t *dev = &g_dev_mgr.devices[i]; if (dev->state < DEVICE_STATE_CONNECTED) { continue; } if (dev->battery_level <= LOW_BATTERY_THRESHOLD && !dev->low_battery_notified) { char mac_str[20]; mac_to_str(dev->mac_addr, mac_str, sizeof(mac_str)); printf("[设备管理] 低电量告警: %s (%s) 电量=%d%%\n", dev->name, mac_str, dev->battery_level); /* 通过MQTT上报低电量事件 */ /* mqtt_publish("gateway/{id}/alert", "{\"type\":\"low_battery\",\"pen\":\"xx\",\"level\":N}"); */ dev->low_battery_notified = true; } /* 电量恢复后重置通知标志 */ if (dev->battery_level > LOW_BATTERY_THRESHOLD + 5) { dev->low_battery_notified = false; } } } /** * 设备状态监控线程 * 定期检查心跳超时和低电量 */ static void *device_monitor_thread(void *arg) { printf("[设备管理] 监控线程启动\n"); while (g_dev_mgr.running) { sleep(DEVICE_CHECK_INTERVAL); pthread_mutex_lock(&g_dev_mgr.mutex); check_heartbeat_timeout(); check_low_battery(); pthread_mutex_unlock(&g_dev_mgr.mutex); } printf("[设备管理] 监控线程退出\n"); return NULL; } /* ======================== 公共接口 ======================== */ /** * 初始化设备管理器 */ int device_manager_init(const char *gateway_id) { memset(&g_dev_mgr, 0, sizeof(g_dev_mgr)); strncpy(g_dev_mgr.gateway_id, gateway_id, sizeof(g_dev_mgr.gateway_id) - 1); pthread_mutex_init(&g_dev_mgr.mutex, NULL); g_dev_mgr.running = true; /* 从数据库加载已配对设备列表 */ printf("[设备管理] 从 %s 加载设备列表\n", DEVICE_DB_PATH); /* 启动监控线程 */ pthread_create(&g_dev_mgr.monitor_thread, NULL, device_monitor_thread, NULL); printf("[设备管理] 初始化完成, 网关=%s, 最大设备=%d\n", gateway_id, MAX_DEVICES); return 0; } /** * 处理BLE扫描发现的设备 * 判断是否为已知设备,新设备则添加到列表 */ int device_manager_on_discovered(const uint8_t mac[6], const char *name, int8_t rssi, const uint8_t *adv_data, uint8_t adv_len) { pthread_mutex_lock(&g_dev_mgr.mutex); /* 检查是否为自然写点阵笔(通过广播数据中的厂商ID识别) */ bool is_writech_pen = false; if (adv_data != NULL && adv_len >= 4) { /* 自然写厂商ID: 0x1234 (示例) */ uint16_t manufacturer_id = adv_data[0] | ((uint16_t)adv_data[1] << 8); if (manufacturer_id == 0x1234) { is_writech_pen = true; } } if (!is_writech_pen) { pthread_mutex_unlock(&g_dev_mgr.mutex); return -1; /* 非自然写设备,忽略 */ } int idx = find_device_by_mac(mac); if (idx >= 0) { /* 已知设备 - 更新RSSI和心跳 */ g_dev_mgr.devices[idx].rssi = rssi; g_dev_mgr.devices[idx].last_heartbeat = time(NULL); if (g_dev_mgr.devices[idx].state == DEVICE_STATE_DISCONNECTED || g_dev_mgr.devices[idx].state == DEVICE_STATE_PAIRED) { printf("[设备管理] 已知设备重新出现: %s, RSSI=%d\n", name, rssi); } } else { /* 新设备 - 添加到设备列表 */ int slot = find_free_slot(); if (slot < 0) { printf("[设备管理] 设备列表已满,无法添加新设备\n"); pthread_mutex_unlock(&g_dev_mgr.mutex); return -2; } device_info_t *dev = &g_dev_mgr.devices[slot]; memcpy(dev->mac_addr, mac, 6); strncpy(dev->name, name ? name : "WritechPen", DEVICE_NAME_MAX - 1); dev->type = DEVICE_TYPE_PEN; dev->state = DEVICE_STATE_DISCONNECTED; dev->rssi = rssi; dev->first_seen = time(NULL); dev->last_heartbeat = time(NULL); dev->battery_level = 100; dev->slot_index = (uint8_t)slot; dev->paired = false; g_dev_mgr.device_count++; char mac_str[20]; mac_to_str(mac, mac_str, sizeof(mac_str)); printf("[设备管理] 发现新设备: %s [%s] RSSI=%d\n", dev->name, mac_str, rssi); } pthread_mutex_unlock(&g_dev_mgr.mutex); return 0; } /** * 更新设备连接状态 */ void device_manager_update_state(const uint8_t mac[6], device_state_t state) { pthread_mutex_lock(&g_dev_mgr.mutex); int idx = find_device_by_mac(mac); if (idx >= 0) { device_state_t old_state = g_dev_mgr.devices[idx].state; g_dev_mgr.devices[idx].state = state; g_dev_mgr.devices[idx].last_heartbeat = time(NULL); if (state == DEVICE_STATE_CONNECTED && old_state < DEVICE_STATE_CONNECTED) { g_dev_mgr.devices[idx].reconnect_count++; printf("[设备管理] 设备 %s 已连接 (第%u次)\n", g_dev_mgr.devices[idx].name, g_dev_mgr.devices[idx].reconnect_count); } g_dev_mgr.total_connected = count_online_devices(); } pthread_mutex_unlock(&g_dev_mgr.mutex); } /** * 更新设备电量信息 */ void device_manager_update_battery(const uint8_t mac[6], uint8_t level) { pthread_mutex_lock(&g_dev_mgr.mutex); int idx = find_device_by_mac(mac); if (idx >= 0) { g_dev_mgr.devices[idx].battery_level = level; g_dev_mgr.devices[idx].last_heartbeat = time(NULL); } pthread_mutex_unlock(&g_dev_mgr.mutex); } /** * 获取所有在线设备信息(JSON格式,用于MQTT状态上报) */ int device_manager_get_status_json(char *json_buf, int buf_size) { pthread_mutex_lock(&g_dev_mgr.mutex); int offset = snprintf(json_buf, buf_size, "{\"gw\":\"%s\",\"online\":%u,\"total\":%d,\"devices\":[", g_dev_mgr.gateway_id, g_dev_mgr.total_connected, g_dev_mgr.device_count); bool first = true; for (int i = 0; i < g_dev_mgr.device_count && offset < buf_size - 128; i++) { device_info_t *dev = &g_dev_mgr.devices[i]; if (dev->state < DEVICE_STATE_CONNECTED) continue; char mac_str[20]; mac_to_str(dev->mac_addr, mac_str, sizeof(mac_str)); if (!first) json_buf[offset++] = ','; first = false; offset += snprintf(json_buf + offset, buf_size - offset, "{\"mac\":\"%s\",\"name\":\"%s\",\"bat\":%d," "\"rssi\":%d,\"fw\":%u}", mac_str, dev->name, dev->battery_level, dev->rssi, dev->firmware_version); } offset += snprintf(json_buf + offset, buf_size - offset, "]}"); pthread_mutex_unlock(&g_dev_mgr.mutex); return offset; } /** * 关闭设备管理器 */ void device_manager_shutdown(void) { g_dev_mgr.running = false; pthread_join(g_dev_mgr.monitor_thread, NULL); /* 保存设备列表到数据库 */ printf("[设备管理] 保存 %d 个设备信息到数据库\n", g_dev_mgr.device_count); pthread_mutex_destroy(&g_dev_mgr.mutex); printf("[设备管理] 已关闭, 累计断连=%u次\n", g_dev_mgr.total_disconnects); }