/* * 自然写互动课堂教学管理网关软件 V1.0 * main.c - 网关主程序入口 * * 功能说明: * 1. 系统初始化与模块启动协调 * 2. 主事件循环(epoll事件驱动模型) * 3. 信号处理与优雅退出 * 4. 系统运行状态监控 * * 硬件平台:ARM Linux嵌入式网关 * 角色:教室内BLE点阵笔 ↔ MQTT云平台的协议桥接 */ #include #include #include #include #include #include #include #include #include #include /* 模块头文件 */ #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; }