Files
system-design/software-copyright/04-writech-gateway/config/gateway_config.c
T
2026-03-22 15:24:40 +08:00

448 lines
15 KiB
C

/**
* 自然写教室智能网关管理软件 V1.0
*
* gateway_config.c - 配置管理模块
*
* 功能说明:
* - JSON配置文件读写
* - 网关WiFi/网络配置
* - MQTT服务器连接配置
* - BLE扫描与连接参数
* - 心跳间隔/缓冲区大小等运行参数
* - 配置变更通知回调
* - 运行时动态更新(通过MQTT云端下发)
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <stdbool.h>
#include <time.h>
#include <sys/stat.h>
/* ======================== 常量定义 ======================== */
/* 配置文件路径 */
#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);
}