333 lines
8.5 KiB
C
333 lines
8.5 KiB
C
/*
|
|
* 自然写互动课堂教学管理网关软件 V1.0
|
|
* main.c - 网关主程序入口
|
|
*
|
|
* 功能说明:
|
|
* 1. 系统初始化与模块启动协调
|
|
* 2. 主事件循环(epoll事件驱动模型)
|
|
* 3. 信号处理与优雅退出
|
|
* 4. 系统运行状态监控
|
|
*
|
|
* 硬件平台:ARM Linux嵌入式网关
|
|
* 角色:教室内BLE点阵笔 ↔ MQTT云平台的协议桥接
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <signal.h>
|
|
#include <unistd.h>
|
|
#include <pthread.h>
|
|
#include <sys/epoll.h>
|
|
#include <sys/time.h>
|
|
#include <syslog.h>
|
|
#include <errno.h>
|
|
|
|
/* 模块头文件 */
|
|
#include "ble_manager.h"
|
|
#include "mqtt_client.h"
|
|
#include "protocol_converter.h"
|
|
#include "ring_buffer.h"
|
|
#include "offline_cache.h"
|
|
#include "device_manager.h"
|
|
#include "ota_updater.h"
|
|
#include "gateway_config.h"
|
|
#include "watchdog.h"
|
|
#include "http_server.h"
|
|
|
|
/* ========== 全局常量 ========== */
|
|
|
|
#define GATEWAY_VERSION "1.0.0"
|
|
#define MAX_EPOLL_EVENTS 64
|
|
#define MAIN_LOOP_TIMEOUT_MS 100
|
|
|
|
/* ========== 全局变量 ========== */
|
|
|
|
/* 运行标志(信号处理中设置为0) */
|
|
static volatile int g_running = 1;
|
|
|
|
/* epoll文件描述符 */
|
|
static int g_epoll_fd = -1;
|
|
|
|
/* 系统启动时间 */
|
|
static struct timeval g_start_time;
|
|
|
|
/* 各模块状态 */
|
|
typedef struct {
|
|
int ble_active; /* BLE模块是否正常 */
|
|
int mqtt_connected; /* MQTT是否已连接 */
|
|
int pen_count; /* 已连接笔数量 */
|
|
int cache_count; /* 离线缓存数据条数 */
|
|
unsigned long uptime_sec; /* 运行时长(秒) */
|
|
unsigned long total_packets;/* 累计转发数据包数 */
|
|
} GatewayStatus;
|
|
|
|
static GatewayStatus g_status;
|
|
|
|
/* ========== 信号处理 ========== */
|
|
|
|
/**
|
|
* 信号处理函数
|
|
* 捕获SIGINT/SIGTERM实现优雅退出
|
|
*/
|
|
static void signal_handler(int signo) {
|
|
if (signo == SIGINT || signo == SIGTERM) {
|
|
syslog(LOG_INFO, "收到终止信号 %d,准备退出...", signo);
|
|
g_running = 0;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 注册信号处理器
|
|
*/
|
|
static void setup_signals(void) {
|
|
struct sigaction sa;
|
|
memset(&sa, 0, sizeof(sa));
|
|
sa.sa_handler = signal_handler;
|
|
sigemptyset(&sa.sa_mask);
|
|
sa.sa_flags = 0;
|
|
|
|
sigaction(SIGINT, &sa, NULL);
|
|
sigaction(SIGTERM, &sa, NULL);
|
|
|
|
/* 忽略SIGPIPE(网络连接断开时避免进程退出) */
|
|
signal(SIGPIPE, SIG_IGN);
|
|
}
|
|
|
|
/* ========== 模块初始化 ========== */
|
|
|
|
/**
|
|
* 初始化所有功能模块
|
|
* 按依赖顺序逐一启动各子系统
|
|
*
|
|
* @return 0成功, -1失败
|
|
*/
|
|
static int init_modules(void) {
|
|
syslog(LOG_INFO, "=== 自然写网关 V%s 启动 ===", GATEWAY_VERSION);
|
|
|
|
/* 步骤1:加载配置文件 */
|
|
if (gateway_config_load("/etc/writech/gateway.conf") != 0) {
|
|
syslog(LOG_WARNING, "配置文件加载失败,使用默认配置");
|
|
gateway_config_load_defaults();
|
|
}
|
|
|
|
/* 步骤2:初始化环形缓冲区(用于BLE→MQTT数据转发) */
|
|
ring_buffer_init(64 * 1024); /* 64KB缓冲区 */
|
|
|
|
/* 步骤3:初始化离线缓存(SQLite) */
|
|
if (offline_cache_init("/var/lib/writech/cache.db") != 0) {
|
|
syslog(LOG_ERR, "离线缓存初始化失败");
|
|
return -1;
|
|
}
|
|
|
|
/* 步骤4:初始化BLE管理器 */
|
|
if (ble_manager_init() != 0) {
|
|
syslog(LOG_ERR, "BLE管理器初始化失败");
|
|
return -1;
|
|
}
|
|
|
|
/* 步骤5:初始化MQTT客户端 */
|
|
const char *mqtt_host = gateway_config_get_string("mqtt.host", "mqtt.writech.com");
|
|
int mqtt_port = gateway_config_get_int("mqtt.port", 8883);
|
|
if (mqtt_client_init(mqtt_host, mqtt_port) != 0) {
|
|
syslog(LOG_ERR, "MQTT客户端初始化失败");
|
|
return -1;
|
|
}
|
|
|
|
/* 步骤6:初始化协议转换器 */
|
|
protocol_converter_init();
|
|
|
|
/* 步骤7:初始化设备管理器 */
|
|
device_manager_init();
|
|
|
|
/* 步骤8:初始化OTA升级模块 */
|
|
ota_updater_init();
|
|
|
|
/* 步骤9:初始化看门狗 */
|
|
watchdog_init(30); /* 30秒超时 */
|
|
|
|
/* 步骤10:启动本地Web管理页面 */
|
|
int http_port = gateway_config_get_int("http.port", 8080);
|
|
http_server_start(http_port);
|
|
|
|
syslog(LOG_INFO, "所有模块初始化完成");
|
|
return 0;
|
|
}
|
|
|
|
/* ========== 主事件循环 ========== */
|
|
|
|
/**
|
|
* 创建epoll实例并注册各模块的文件描述符
|
|
*/
|
|
static int setup_epoll(void) {
|
|
g_epoll_fd = epoll_create1(0);
|
|
if (g_epoll_fd < 0) {
|
|
syslog(LOG_ERR, "epoll_create失败: %s", strerror(errno));
|
|
return -1;
|
|
}
|
|
|
|
/* 注册BLE事件文件描述符 */
|
|
int ble_fd = ble_manager_get_fd();
|
|
if (ble_fd >= 0) {
|
|
struct epoll_event ev;
|
|
ev.events = EPOLLIN;
|
|
ev.data.fd = ble_fd;
|
|
epoll_ctl(g_epoll_fd, EPOLL_CTL_ADD, ble_fd, &ev);
|
|
}
|
|
|
|
/* 注册MQTT事件文件描述符 */
|
|
int mqtt_fd = mqtt_client_get_fd();
|
|
if (mqtt_fd >= 0) {
|
|
struct epoll_event ev;
|
|
ev.events = EPOLLIN | EPOLLOUT;
|
|
ev.data.fd = mqtt_fd;
|
|
epoll_ctl(g_epoll_fd, EPOLL_CTL_ADD, mqtt_fd, &ev);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* 处理epoll事件
|
|
*/
|
|
static void process_events(struct epoll_event *events, int count) {
|
|
int i;
|
|
for (i = 0; i < count; i++) {
|
|
int fd = events[i].data.fd;
|
|
|
|
if (fd == ble_manager_get_fd()) {
|
|
/* BLE数据就绪,读取并转发 */
|
|
ble_manager_process_events();
|
|
} else if (fd == mqtt_client_get_fd()) {
|
|
/* MQTT事件处理 */
|
|
if (events[i].events & EPOLLIN) {
|
|
mqtt_client_process_read();
|
|
}
|
|
if (events[i].events & EPOLLOUT) {
|
|
mqtt_client_process_write();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 定时任务处理(每次主循环迭代执行)
|
|
* 处理非事件驱动的周期性任务
|
|
*/
|
|
static void periodic_tasks(void) {
|
|
static unsigned long tick_count = 0;
|
|
tick_count++;
|
|
|
|
/* 每秒执行一次 */
|
|
if (tick_count % 10 == 0) {
|
|
/* 喂看门狗 */
|
|
watchdog_feed();
|
|
|
|
/* 更新运行时长 */
|
|
struct timeval now;
|
|
gettimeofday(&now, NULL);
|
|
g_status.uptime_sec = now.tv_sec - g_start_time.tv_sec;
|
|
}
|
|
|
|
/* 每5秒执行一次 */
|
|
if (tick_count % 50 == 0) {
|
|
/* 更新状态信息 */
|
|
g_status.ble_active = ble_manager_is_active();
|
|
g_status.mqtt_connected = mqtt_client_is_connected();
|
|
g_status.pen_count = ble_manager_get_connected_count();
|
|
g_status.cache_count = offline_cache_get_count();
|
|
}
|
|
|
|
/* 每30秒执行一次 */
|
|
if (tick_count % 300 == 0) {
|
|
/* 尝试回传离线缓存数据 */
|
|
if (g_status.mqtt_connected && g_status.cache_count > 0) {
|
|
offline_cache_flush_to_mqtt();
|
|
}
|
|
|
|
/* 检查OTA更新 */
|
|
ota_updater_check();
|
|
}
|
|
|
|
/* 协议转发:从环形缓冲区读取BLE数据,转换后发送到MQTT */
|
|
protocol_converter_process();
|
|
}
|
|
|
|
/* ========== 清理退出 ========== */
|
|
|
|
/**
|
|
* 清理并释放所有资源
|
|
*/
|
|
static void cleanup(void) {
|
|
syslog(LOG_INFO, "开始清理资源...");
|
|
|
|
http_server_stop();
|
|
watchdog_stop();
|
|
ota_updater_cleanup();
|
|
device_manager_cleanup();
|
|
mqtt_client_cleanup();
|
|
ble_manager_cleanup();
|
|
offline_cache_close();
|
|
ring_buffer_destroy();
|
|
gateway_config_free();
|
|
|
|
if (g_epoll_fd >= 0) {
|
|
close(g_epoll_fd);
|
|
}
|
|
|
|
syslog(LOG_INFO, "=== 网关已安全退出 ===");
|
|
closelog();
|
|
}
|
|
|
|
/* ========== 主函数 ========== */
|
|
|
|
int main(int argc, char *argv[]) {
|
|
/* 打开系统日志 */
|
|
openlog("writech-gateway", LOG_PID | LOG_NDELAY, LOG_DAEMON);
|
|
|
|
/* 记录启动时间 */
|
|
gettimeofday(&g_start_time, NULL);
|
|
memset(&g_status, 0, sizeof(g_status));
|
|
|
|
/* 注册信号处理 */
|
|
setup_signals();
|
|
|
|
/* 初始化所有模块 */
|
|
if (init_modules() != 0) {
|
|
syslog(LOG_ERR, "模块初始化失败,退出");
|
|
cleanup();
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
/* 设置epoll */
|
|
if (setup_epoll() != 0) {
|
|
cleanup();
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
/* 主事件循环 */
|
|
struct epoll_event events[MAX_EPOLL_EVENTS];
|
|
|
|
syslog(LOG_INFO, "进入主事件循环...");
|
|
|
|
while (g_running) {
|
|
int nfds = epoll_wait(g_epoll_fd, events, MAX_EPOLL_EVENTS,
|
|
MAIN_LOOP_TIMEOUT_MS);
|
|
|
|
if (nfds < 0) {
|
|
if (errno == EINTR) continue;
|
|
syslog(LOG_ERR, "epoll_wait错误: %s", strerror(errno));
|
|
break;
|
|
}
|
|
|
|
if (nfds > 0) {
|
|
process_events(events, nfds);
|
|
}
|
|
|
|
periodic_tasks();
|
|
}
|
|
|
|
cleanup();
|
|
return EXIT_SUCCESS;
|
|
}
|