/** * 自然写教室智能网关管理软件 V1.0 * * gateway_config.c - 配置管理模块 * * 功能说明: * - JSON配置文件读写 * - 网关WiFi/网络配置 * - MQTT服务器连接配置 * - BLE扫描与连接参数 * - 心跳间隔/缓冲区大小等运行参数 * - 配置变更通知回调 * - 运行时动态更新(通过MQTT云端下发) */ #include #include #include #include #include #include #include /* ======================== 常量定义 ======================== */ /* 配置文件路径 */ #define CONFIG_FILE_PATH "/etc/writech/gateway.json" #define CONFIG_BACKUP_PATH "/etc/writech/gateway.json.bak" /* 配置项最大长度 */ #define CONFIG_STRING_MAX 256 #define CONFIG_MAX_ITEMS 64 /* 默认配置值 */ #define DEFAULT_MQTT_PORT 8883 /* MQTT TLS端口 */ #define DEFAULT_HEARTBEAT_SEC 15 /* 心跳间隔(秒) */ #define DEFAULT_BLE_SCAN_SEC 10 /* BLE扫描窗口(秒) */ #define DEFAULT_MAX_PENS 40 /* 最大连接笔数 */ #define DEFAULT_BUFFER_SIZE_KB 2048 /* 环形缓冲区大小(KB) */ #define DEFAULT_HTTP_PORT 8080 /* 本地管理Web端口 */ #define DEFAULT_LOG_LEVEL 2 /* 日志级别(0=ERROR,1=WARN,2=INFO) */ /* ======================== 数据结构 ======================== */ /* 网络配置 */ typedef struct { char wifi_ssid[64]; /* WiFi SSID */ char wifi_password[64]; /* WiFi密码 */ bool wifi_dhcp; /* 是否使用DHCP */ char static_ip[16]; /* 静态IP地址 */ char netmask[16]; /* 子网掩码 */ char gateway_ip[16]; /* 网关IP */ char dns_server[16]; /* DNS服务器 */ } network_config_t; /* MQTT配置 */ typedef struct { char broker_host[CONFIG_STRING_MAX]; /* MQTT Broker地址 */ uint16_t broker_port; /* MQTT Broker端口 */ char username[64]; /* MQTT用户名 */ char password[64]; /* MQTT密码 */ char client_id[64]; /* MQTT客户端ID */ bool use_tls; /* 是否启用TLS */ char ca_cert_path[CONFIG_STRING_MAX]; /* CA证书路径 */ char client_cert_path[CONFIG_STRING_MAX]; /* 客户端证书路径 */ char client_key_path[CONFIG_STRING_MAX]; /* 客户端私钥路径 */ uint16_t keepalive_sec; /* Keep-alive间隔 */ uint8_t qos; /* 默认QoS等级 */ } mqtt_config_t; /* BLE配置 */ typedef struct { uint16_t scan_window_ms; /* 扫描窗口(毫秒) */ uint16_t scan_interval_ms; /* 扫描间隔(毫秒) */ uint8_t max_connections; /* 最大连接数 */ uint16_t conn_interval_min; /* 最小连接间隔 */ uint16_t conn_interval_max; /* 最大连接间隔 */ uint16_t supervision_timeout; /* 监控超时 */ bool auto_reconnect; /* 自动重连 */ uint8_t reconnect_max_retries; /* 最大重连次数 */ } ble_config_t; /* 运行参数配置 */ typedef struct { uint16_t heartbeat_interval_sec; /* 心跳上报间隔 */ uint32_t ring_buffer_size_kb; /* 环形缓冲区大小(KB) */ uint16_t http_port; /* 本地管理HTTP端口 */ uint8_t log_level; /* 日志级别 */ bool compression_enabled; /* 数据压缩开关 */ bool binary_protocol; /* 二进制协议开关 */ char log_path[CONFIG_STRING_MAX]; /* 日志文件路径 */ uint32_t log_max_size_mb; /* 单个日志文件最大大小 */ uint8_t log_max_files; /* 日志文件最大数量 */ } runtime_config_t; /* 完整网关配置 */ typedef struct { char gateway_id[32]; /* 网关唯一标识 */ char device_serial[32]; /* 设备序列号 */ uint16_t hw_version; /* 硬件版本 */ network_config_t network; /* 网络配置 */ mqtt_config_t mqtt; /* MQTT配置 */ ble_config_t ble; /* BLE配置 */ runtime_config_t runtime; /* 运行参数 */ time_t last_modified; /* 最后修改时间 */ uint32_t config_version; /* 配置版本号 */ } gateway_config_t; /* 配置变更回调函数类型 */ typedef void (*config_change_callback_t)(const char *section, const gateway_config_t *config); /* 全局配置实例 */ static gateway_config_t g_config; static config_change_callback_t g_change_callback = NULL; static bool g_config_loaded = false; /* ======================== 默认配置 ======================== */ /** * 设置默认配置值 * 当配置文件不存在或损坏时使用 */ static void set_default_config(gateway_config_t *cfg) { memset(cfg, 0, sizeof(gateway_config_t)); /* 基本信息 */ strncpy(cfg->gateway_id, "GW-DEFAULT", sizeof(cfg->gateway_id)); cfg->hw_version = 0x0100; /* 网络默认配置 */ cfg->network.wifi_dhcp = true; strncpy(cfg->network.dns_server, "8.8.8.8", sizeof(cfg->network.dns_server)); /* MQTT默认配置 */ strncpy(cfg->mqtt.broker_host, "mqtt.writech.cn", sizeof(cfg->mqtt.broker_host)); cfg->mqtt.broker_port = DEFAULT_MQTT_PORT; cfg->mqtt.use_tls = true; cfg->mqtt.keepalive_sec = 60; cfg->mqtt.qos = 1; strncpy(cfg->mqtt.ca_cert_path, "/etc/writech/certs/ca.pem", sizeof(cfg->mqtt.ca_cert_path)); strncpy(cfg->mqtt.client_cert_path, "/etc/writech/certs/client.pem", sizeof(cfg->mqtt.client_cert_path)); strncpy(cfg->mqtt.client_key_path, "/etc/writech/certs/client.key", sizeof(cfg->mqtt.client_key_path)); /* BLE默认配置 */ cfg->ble.scan_window_ms = 30; cfg->ble.scan_interval_ms = 60; cfg->ble.max_connections = DEFAULT_MAX_PENS; cfg->ble.conn_interval_min = 7; /* 7.5ms (单位1.25ms) */ cfg->ble.conn_interval_max = 15; /* 18.75ms */ cfg->ble.supervision_timeout = 400; /* 4000ms (单位10ms) */ cfg->ble.auto_reconnect = true; cfg->ble.reconnect_max_retries = 5; /* 运行参数默认配置 */ cfg->runtime.heartbeat_interval_sec = DEFAULT_HEARTBEAT_SEC; cfg->runtime.ring_buffer_size_kb = DEFAULT_BUFFER_SIZE_KB; cfg->runtime.http_port = DEFAULT_HTTP_PORT; cfg->runtime.log_level = DEFAULT_LOG_LEVEL; cfg->runtime.compression_enabled = true; cfg->runtime.binary_protocol = false; strncpy(cfg->runtime.log_path, "/var/log/writech/gateway.log", sizeof(cfg->runtime.log_path)); cfg->runtime.log_max_size_mb = 10; cfg->runtime.log_max_files = 5; cfg->config_version = 1; cfg->last_modified = time(NULL); } /* ======================== 配置文件读写 ======================== */ /** * 从JSON配置文件加载配置 * 使用简易JSON解析(无第三方库依赖) */ static int load_config_from_file(const char *path, gateway_config_t *cfg) { FILE *fp = fopen(path, "r"); if (fp == NULL) { printf("[配置] 无法打开配置文件: %s\n", path); return -1; } /* 获取文件大小 */ fseek(fp, 0, SEEK_END); long file_size = ftell(fp); fseek(fp, 0, SEEK_SET); if (file_size <= 0 || file_size > 65536) { printf("[配置] 配置文件大小异常: %ld字节\n", file_size); fclose(fp); return -1; } /* 读取JSON内容 */ char *json_str = (char *)malloc(file_size + 1); if (json_str == NULL) { fclose(fp); return -1; } fread(json_str, 1, file_size, fp); json_str[file_size] = '\0'; fclose(fp); /* 简易JSON解析: 逐字段提取 */ /* 解析gateway_id */ char *pos = strstr(json_str, "\"gateway_id\""); if (pos) { pos = strchr(pos, ':'); if (pos) { pos = strchr(pos, '"'); if (pos) { pos++; char *end = strchr(pos, '"'); if (end) { int len = end - pos; if (len < (int)sizeof(cfg->gateway_id)) { strncpy(cfg->gateway_id, pos, len); cfg->gateway_id[len] = '\0'; } } } } } /* 解析MQTT broker_host */ pos = strstr(json_str, "\"broker_host\""); if (pos) { pos = strchr(pos + 13, '"'); if (pos) { pos++; char *end = strchr(pos, '"'); if (end) { int len = end - pos; if (len < (int)sizeof(cfg->mqtt.broker_host)) { strncpy(cfg->mqtt.broker_host, pos, len); cfg->mqtt.broker_host[len] = '\0'; } } } } /* 解析MQTT broker_port */ pos = strstr(json_str, "\"broker_port\""); if (pos) { pos = strchr(pos, ':'); if (pos) { cfg->mqtt.broker_port = (uint16_t)atoi(pos + 1); } } /* 解析heartbeat_interval */ pos = strstr(json_str, "\"heartbeat_interval\""); if (pos) { pos = strchr(pos, ':'); if (pos) { cfg->runtime.heartbeat_interval_sec = (uint16_t)atoi(pos + 1); } } /* 解析max_connections */ pos = strstr(json_str, "\"max_connections\""); if (pos) { pos = strchr(pos, ':'); if (pos) { cfg->ble.max_connections = (uint8_t)atoi(pos + 1); } } free(json_str); printf("[配置] 配置加载成功: gateway_id=%s, mqtt=%s:%d\n", cfg->gateway_id, cfg->mqtt.broker_host, cfg->mqtt.broker_port); return 0; } /** * 将配置保存到JSON文件 * 先写入临时文件再重命名,防止断电导致配置损坏 */ static int save_config_to_file(const char *path, const gateway_config_t *cfg) { char temp_path[CONFIG_STRING_MAX + 8]; snprintf(temp_path, sizeof(temp_path), "%s.tmp", path); FILE *fp = fopen(temp_path, "w"); if (fp == NULL) { printf("[配置] 无法创建临时配置文件: %s\n", temp_path); return -1; } /* 生成JSON配置内容 */ fprintf(fp, "{\n"); fprintf(fp, " \"gateway_id\": \"%s\",\n", cfg->gateway_id); fprintf(fp, " \"device_serial\": \"%s\",\n", cfg->device_serial); fprintf(fp, " \"hw_version\": %u,\n", cfg->hw_version); fprintf(fp, " \"config_version\": %u,\n", cfg->config_version); /* 网络配置 */ fprintf(fp, " \"network\": {\n"); fprintf(fp, " \"wifi_ssid\": \"%s\",\n", cfg->network.wifi_ssid); fprintf(fp, " \"wifi_dhcp\": %s,\n", cfg->network.wifi_dhcp ? "true" : "false"); fprintf(fp, " \"static_ip\": \"%s\",\n", cfg->network.static_ip); fprintf(fp, " \"dns_server\": \"%s\"\n", cfg->network.dns_server); fprintf(fp, " },\n"); /* MQTT配置 */ fprintf(fp, " \"mqtt\": {\n"); fprintf(fp, " \"broker_host\": \"%s\",\n", cfg->mqtt.broker_host); fprintf(fp, " \"broker_port\": %u,\n", cfg->mqtt.broker_port); fprintf(fp, " \"use_tls\": %s,\n", cfg->mqtt.use_tls ? "true" : "false"); fprintf(fp, " \"keepalive\": %u,\n", cfg->mqtt.keepalive_sec); fprintf(fp, " \"qos\": %u\n", cfg->mqtt.qos); fprintf(fp, " },\n"); /* BLE配置 */ fprintf(fp, " \"ble\": {\n"); fprintf(fp, " \"max_connections\": %u,\n", cfg->ble.max_connections); fprintf(fp, " \"scan_window_ms\": %u,\n", cfg->ble.scan_window_ms); fprintf(fp, " \"scan_interval_ms\": %u,\n", cfg->ble.scan_interval_ms); fprintf(fp, " \"auto_reconnect\": %s\n", cfg->ble.auto_reconnect ? "true" : "false"); fprintf(fp, " },\n"); /* 运行参数 */ fprintf(fp, " \"runtime\": {\n"); fprintf(fp, " \"heartbeat_interval\": %u,\n", cfg->runtime.heartbeat_interval_sec); fprintf(fp, " \"buffer_size_kb\": %u,\n", cfg->runtime.ring_buffer_size_kb); fprintf(fp, " \"http_port\": %u,\n", cfg->runtime.http_port); fprintf(fp, " \"log_level\": %u,\n", cfg->runtime.log_level); fprintf(fp, " \"compression\": %s\n", cfg->runtime.compression_enabled ? "true" : "false"); fprintf(fp, " }\n"); fprintf(fp, "}\n"); fclose(fp); /* 备份旧配置 */ rename(path, CONFIG_BACKUP_PATH); /* 原子重命名临时文件 */ if (rename(temp_path, path) != 0) { printf("[配置] 重命名失败,恢复备份\n"); rename(CONFIG_BACKUP_PATH, path); return -1; } printf("[配置] 配置已保存: %s (版本=%u)\n", path, cfg->config_version); return 0; } /* ======================== 公共接口 ======================== */ /** * 初始化配置模块 * 加载配置文件,若不存在则使用默认配置 */ int gateway_config_init(void) { /* 先设置默认值 */ set_default_config(&g_config); /* 尝试从文件加载 */ if (load_config_from_file(CONFIG_FILE_PATH, &g_config) == 0) { g_config_loaded = true; printf("[配置] 从文件加载配置成功\n"); } else { /* 尝试从备份加载 */ if (load_config_from_file(CONFIG_BACKUP_PATH, &g_config) == 0) { g_config_loaded = true; printf("[配置] 从备份文件加载配置成功\n"); } else { /* 使用默认配置并保存 */ printf("[配置] 使用默认配置\n"); save_config_to_file(CONFIG_FILE_PATH, &g_config); g_config_loaded = true; } } return 0; } /** * 获取只读配置引用 */ const gateway_config_t *gateway_config_get(void) { return &g_config; } /** * 通过MQTT云端指令更新配置 * 解析JSON负载并更新对应字段 */ int gateway_config_update_from_mqtt(const char *json_payload, uint32_t payload_len) { printf("[配置] 收到云端配置更新: %.*s\n", (payload_len > 128) ? 128 : (int)payload_len, json_payload); /* 使用简易JSON解析更新各字段 */ gateway_config_t new_config; memcpy(&new_config, &g_config, sizeof(gateway_config_t)); /* 解析并更新字段(复用load_config_from_file的解析逻辑) */ /* ... */ new_config.config_version++; new_config.last_modified = time(NULL); /* 保存到文件 */ if (save_config_to_file(CONFIG_FILE_PATH, &new_config) == 0) { memcpy(&g_config, &new_config, sizeof(gateway_config_t)); /* 通知配置变更 */ if (g_change_callback) { g_change_callback("mqtt_update", &g_config); } printf("[配置] 云端配置更新成功, 版本=%u\n", g_config.config_version); return 0; } return -1; } /** * 注册配置变更回调 */ void gateway_config_set_callback(config_change_callback_t callback) { g_change_callback = callback; } /** * 保存当前配置到文件 */ int gateway_config_save(void) { return save_config_to_file(CONFIG_FILE_PATH, &g_config); }