物联网综合性应用之网关设计
最近由于项目的需要,设计了一款基于rv1106的物联网网关
该产品需要集合IOT功能 BLE定位 能耗监测io控制等,音视频通话(基于webrtc)
由于概念机阶段不想自己开板于是乎找到万能的淘宝相中了顶配版的luckfox pico w.
理由是
1 、支持音视频编码,
2、有wifi ble5重点是有poe可以直接工程化
3、配置的屏4.3寸720还比较细腻,
4、串口+扩rs485 modbus
4、但喇叭和mic差强人意,摄像头也只能勉强能用
5、不过幸狐的sdk是加分项,集成了很多有用的软件组件比如lvgl还有很多例程,比如基于tuya的ipc web应用.
当然作为一个学物理出身的理工男,比较推崇马斯克的第一性原理,一切黑盒的库和需要用到云平台的方案都是兴奋剂,不能吃。于是乎自己动手,丰衣足食,核心通信采用mqtt以及webrtc,嵌入式的kvs改了信令对接自己的平台,实现部署自由,不得不说他很强大。蓝牙定位采用blueZ的ble扫描获取rssi,扫描频度上不去,怀疑这w800有点掉包的嫌疑,这个rssi飘移是个头疼的问题,kalman滤波也用上了,感觉很难有理想状态,还需要多种手段调优,有经验的大佬可以教教我。
最后出来的效果
动态更新界面
前端展示
实时音视频
在开发过程中最大的收获是ai给我的帮助,特别是deepseek的思维链独白给我很强的震撼,最后给出几段比较经典的在ai帮助下写就的代码大家可以评判一下
mqtt 订阅回调
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <stdbool.h>
#include "mqtt.h"
#include "reconnect_subscriber.h"
// 定义回调函数节点
typedef struct {
char topic[256]; // 订阅的主题
iotgateway_message_callback callback; // 回调函数
} CallbackNode;
// 定义回调函数列表
#define MAX_CALLBACKS 10
CallbackNode callback_list[MAX_CALLBACKS];
int callback_count = 0;
// 定义消息结构
typedef struct {
char topic[256]; // 主题
char message[1024]; // 消息内容
} MQTTMessage;
// 定义消息队列
#define MAX_QUEUE_SIZE 100
MQTTMessage message_queue[MAX_QUEUE_SIZE];
int queue_front = 0;
int queue_rear = 0;
pthread_mutex_t callback_mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t queue_mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t queue_cond = PTHREAD_COND_INITIALIZER;
// 消息入队
void enqueue_message(const char* topic, const char* message) {
pthread_mutex_lock(&queue_mutex);
if ((queue_rear + 1) % MAX_QUEUE_SIZE != queue_front) { // 检查队列是否已满
strncpy(message_queue[queue_rear].topic, topic, sizeof(message_queue[queue_rear].topic) - 1);
strncpy(message_queue[queue_rear].message, message, sizeof(message_queue[queue_rear].message) - 1);
queue_rear = (queue_rear + 1) % MAX_QUEUE_SIZE;
pthread_cond_signal(&queue_cond); // 通知工作线程有新消息
} else {
printf("Message queue is full, dropping message: %s\n", topic);
}
pthread_mutex_unlock(&queue_mutex);
}
// 消息出队
bool dequeue_message(MQTTMessage* msg) {
pthread_mutex_lock(&queue_mutex);
while (queue_front == queue_rear) { // 队列为空,等待新消息
pthread_cond_wait(&queue_cond, &queue_mutex);
}
*msg = message_queue[queue_front];
queue_front = (queue_front + 1) % MAX_QUEUE_SIZE;
pthread_mutex_unlock(&queue_mutex);
return true;
}
// MQTT 消息回调函数,该函数是承接 MQTT 消息的入口函数,在reconnect_subscriber.c中调用,并在本线程中设置成回调函数
void iotgateway_disponse_callback(const char* topic, const char* message, int length) {
printf("Received message from topic: %s\n", topic);
printf("Message content: %s\n", message);
enqueue_message(topic, message); // 将消息推入队列
}
// 添加订阅和回调函数,方便其他应用模块调用,并自动订阅主题
void add_subscribe_and_callback(const char* topic, iotgateway_message_callback callback) {
pthread_mutex_lock(&callback_mutex);
if (callback_count >= MAX_CALLBACKS) {
printf("Error: Callback list is full\n");
pthread_mutex_unlock(&callback_mutex);
return;
}
// 添加回调函数到列表
strncpy(callback_list[callback_count].topic, topic, sizeof(callback_list[callback_count].topic) - 1);
callback_list[callback_count].callback = callback;
callback_count++;
// 订阅主题
add_subscribe_to_iotgateway_topic(topic);
printf("Subscribed to topic: %s\n", topic);
pthread_mutex_unlock(&callback_mutex);
}
// 消息处理线程
//其他地方需要处理订阅的自行添加订阅主题,并设置相应的回调函数
void* iotgateway_mqtt_message_thread(void* arg) {
set_mqtt_callback(iotgateway_disponse_callback); // 设置 MQTT 消息回调函数
while (true) {
MQTTMessage msg;
dequeue_message(&msg); // 从队列中取出消息
printf("Processing message from topic: %s\n", msg.topic);
printf("Message content: %s\n", msg.message);
// 调用匹配的回调函数
pthread_mutex_lock(&callback_mutex);
for (int i = 0; i < callback_count; i++) {
if (strstr(msg.topic, callback_list[i].topic) != NULL) {
callback_list[i].callback(msg.topic, msg.message, strlen(msg.message));
}
}
pthread_mutex_unlock(&callback_mutex);
}
return NULL;
}
yaml配置
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <sys/stat.h>
#include <errno.h>
#include <yaml.h>
#include <syslog.h>
#include <unistd.h> // 用于 getcwd
#include "myconfig.h"
static ConfigT globalConfig;
static ParseState parse_state = { .current_parent = "",.current_key="", .in_sequence = false, .current_list = NULL };
// 定义配置项映射
static KeyValue webrtcconfig[] = {
{"mqtt", &globalConfig, STRUCT_TYPE_NAME, NULL},
{"organization", &(globalConfig.Mqtt.Organization), STRING_TYPE_NAME, "mqtt"},
{"subtopic", &(globalConfig.Mqtt.SUBTOPIC), STRING_TYPE_NAME, "mqtt"},
{"pubtopic", &(globalConfig.Mqtt.PUBTOPIC), STRING_TYPE_NAME, "mqtt"},
{"qos", &(globalConfig.Mqtt.QOS), INT_TYPE_NAME, "mqtt"},
{"serveraddress", &(globalConfig.Mqtt.SERVERADDRESS), STRING_TYPE_NAME, "mqtt"},
{"clientid", &(globalConfig.Mqtt.CLIENTID), STRING_TYPE_NAME, "mqtt"},
{"hearttime", &(globalConfig.Mqtt.HEARTTIME), INT_TYPE_NAME, "mqtt"},
{"log", &globalConfig.Log, STRUCT_TYPE_NAME, NULL},
{"loglevel", &(globalConfig.Log.LogLevel), STRING_TYPE_NAME, "log"},
{"logfilepath", &(globalConfig.Log.LogFilePath), STRING_TYPE_NAME, "log"},
{"logmaxsize", &(globalConfig.Log.LogMaxSize), INT_TYPE_NAME, "log"},
{"wifi", &globalConfig.Wifi, STRUCT_TYPE_NAME, NULL},
{"enable", &(globalConfig.Wifi.Enable), BOOL_TYPE_NAME, "wifi"},
{"ssid", &(globalConfig.Wifi.SSID), STRING_TYPE_NAME, "wifi"},
{"password", &(globalConfig.Wifi.Password), STRING_TYPE_NAME, "wifi"},
{"autoconnect", &(globalConfig.Wifi.AutoConnect), BOOL_TYPE_NAME, "wifi"},
{"retryinterval", &(globalConfig.Wifi.RetryInterval), INT_TYPE_NAME, "wifi"},
{"ble", &globalConfig.BLE, STRUCT_TYPE_NAME, NULL},
{"enable", &(globalConfig.BLE.Enable), BOOL_TYPE_NAME, "ble"},
{"scanprefix", &(globalConfig.BLE.ScanPrefix), LIST_TYPE_NAME, "ble"},
{"scaninterval", &(globalConfig.BLE.ScanInterval), INT_TYPE_NAME, "ble"},
// {"scantimeout", &(globalConfig.BLE.ScanTimeout), INT_TYPE_NAME, "ble"},
{"scanwindow", &(globalConfig.BLE.ScanWindow), INT_TYPE_NAME, "ble"},
{"a_value", &(globalConfig.BLE.AValue), FLOAT_TYPE_NAME, "ble"},
{"n_value", &(globalConfig.BLE.NValue), FLOAT_TYPE_NAME, "ble"},
{"pdu", &globalConfig.PDU, STRUCT_TYPE_NAME, NULL},
{"enable", &(globalConfig.PDU.Enable), BOOL_TYPE_NAME, "pdu"},
{"addr", &(globalConfig.PDU.Addr), STRING_TYPE_NAME, "pdu"},
{"port", &(globalConfig.PDU.Port), INT_TYPE_NAME, "pdu"},
{"baudrate", &(globalConfig.PDU.BaudRate), INT_TYPE_NAME, "pdu"},
{"databits", &(globalConfig.PDU.DataBits), INT_TYPE_NAME, "pdu"},
{"stopbits", &(globalConfig.PDU.StopBits), INT_TYPE_NAME, "pdu"},
{"parity", &(globalConfig.PDU.Parity), STRING_TYPE_NAME, "pdu"},
{"collectinterval", &(globalConfig.PDU.CollectInterval), INT_TYPE_NAME, "pdu"},
{"realpower", &globalConfig.RealPower, STRUCT_TYPE_NAME, NULL},
{"enable", &(globalConfig.RealPower.Enable), BOOL_TYPE_NAME, "realpower"},
{"addr", &(globalConfig.RealPower.Addr), STRING_TYPE_NAME, "realpower"},
{"port", &(globalConfig.RealPower.Port), INT_TYPE_NAME, "realpower"},
{"baudrate", &(globalConfig.RealPower.BaudRate), INT_TYPE_NAME, "realpower"},
{"databits", &(globalConfig.RealPower.DataBits), INT_TYPE_NAME, "realpower"},
{"stopbits", &(globalConfig.RealPower.StopBits), INT_TYPE_NAME, "realpower"},
{"parity", &(globalConfig.RealPower.Parity), STRING_TYPE_NAME, "realpower"},
{"collectinterval", &(globalConfig.RealPower.CollectInterval), INT_TYPE_NAME, "realpower"},
{"webrtc", &globalConfig.WebRTC, STRUCT_TYPE_NAME, NULL},
{"enable", &(globalConfig.WebRTC.Enable), BOOL_TYPE_NAME, "webrtc"},
{"signalingserver", &(globalConfig.WebRTC.SignalingServer), STRING_TYPE_NAME, "webrtc"},
{"iceserver", &(globalConfig.WebRTC.ICEServer), STRING_TYPE_NAME, "webrtc"},
{"iplist", &globalConfig.IPList, LIST_TYPE_NAME, NULL}, // IP 地址列表
{NULL, NULL, 0, NULL},
};
// 当前解析的键
// static char currentkey[100];
// 打印配置
int printConfig(ConfigT *pconfig) {
if (pconfig == NULL) return -1;
printf("MQTT Configuration:\n");
if (pconfig->Mqtt.Organization != NULL) printf(" Organization: %s\n", pconfig->Mqtt.Organization);
if (pconfig->Mqtt.SUBTOPIC != NULL) printf(" subtopic: %s\n", pconfig->Mqtt.SUBTOPIC);
if (pconfig->Mqtt.PUBTOPIC != NULL) printf(" pubtopic: %s\n", pconfig->Mqtt.PUBTOPIC);
printf(" qos: %d\n", pconfig->Mqtt.QOS);
if (pconfig->Mqtt.SERVERADDRESS != NULL) printf(" serveraddress: %s\n", pconfig->Mqtt.SERVERADDRESS);
if (pconfig->Mqtt.CLIENTID != NULL) printf(" clientid: %s\n", pconfig->Mqtt.CLIENTID);
printf(" hearttime: %d\n", pconfig->Mqtt.HEARTTIME);
printf("Log Configuration:\n");
if (pconfig->Log.LogLevel != NULL) printf(" loglevel: %s\n", pconfig->Log.LogLevel);
if (pconfig->Log.LogFilePath != NULL) printf(" logfilepath: %s\n", pconfig->Log.LogFilePath);
printf(" logmaxsize: %d\n", pconfig->Log.LogMaxSize);
printf("WiFi Configuration:\n");
printf(" enable: %s\n", pconfig->Wifi.Enable ? "true" : "false");
if (pconfig->Wifi.SSID != NULL) printf(" ssid: %s\n", pconfig->Wifi.SSID);
if (pconfig->Wifi.Password != NULL) printf(" password: %s\n", pconfig->Wifi.Password);
printf(" autoconnect: %s\n", pconfig->Wifi.AutoConnect ? "true" : "false");
printf(" retryinterval: %d\n", pconfig->Wifi.RetryInterval);
printf("BLE Configuration:\n");
printf(" enable: %s\n", pconfig->BLE.Enable ? "true" : "false");
printf(" scanprefix count:%d\n",pconfig->BLE.ScanPrefix.Count);
for (int i = 0; i < pconfig->BLE.ScanPrefix.Count; i++) {
printf(" scanprefix%d: %s\n", i + 1, pconfig->BLE.ScanPrefix.Items[i]);
}
printf(" scaninterval: %d\n", pconfig->BLE.ScanInterval);
printf(" scanwindow: %d\n", pconfig->BLE.ScanWindow);
printf(" a_value: %f\n", pconfig->BLE.AValue);
printf(" n_value: %f\n", pconfig->BLE.NValue);
// printf(" scantimeout: %d\n", pconfig->BLE.ScanTimeout);
printf("PDU Configuration:\n");
printf(" enable: %s\n", pconfig->PDU.Enable ? "true" : "false");
if (pconfig->PDU.Addr != NULL) printf(" ip: %s\n", pconfig->PDU.Addr);
printf(" port: %d\n", pconfig->PDU.Port);
printf(" baudrate: %d\n", pconfig->PDU.BaudRate);
printf(" databits: %d\n", pconfig->PDU.DataBits);
printf(" stopbits: %d\n", pconfig->PDU.StopBits);
if (pconfig->PDU.Parity != NULL) printf(" parity: %s\n", pconfig->PDU.Parity);
printf(" collectinterval: %d\n", pconfig->PDU.CollectInterval);
printf("RealPower Configuration:\n");
printf(" enable: %s\n", pconfig->RealPower.Enable ? "true" : "false");
if (pconfig->RealPower.Addr != NULL) printf(" ip: %s\n", pconfig->RealPower.Addr);
printf(" port: %d\n", pconfig->RealPower.Port);
printf(" baudrate: %d\n", pconfig->RealPower.BaudRate);
printf(" databits: %d\n", pconfig->RealPower.DataBits);
printf(" stopbits: %d\n", pconfig->RealPower.StopBits);
if (pconfig->RealPower.Parity != NULL) printf(" parity: %s\n", pconfig->PDU.Parity);
printf(" collectinterval: %d\n", pconfig->RealPower.CollectInterval);
printf("WebRTC Configuration:\n");
printf(" enable: %s\n", pconfig->WebRTC.Enable ? "true" : "false");
if (pconfig->WebRTC.SignalingServer != NULL) printf(" signalingserver: %s\n", pconfig->WebRTC.SignalingServer);
if (pconfig->WebRTC.ICEServer != NULL) printf(" iceserver: %s\n", pconfig->WebRTC.ICEServer);
printf("IP List Configuration:\n");
for (int i = 0; i < pconfig->IPList.Count; i++) {
printf(" ip%d: %s\n", i + 1, pconfig->IPList.Items[i]);
}
return 0;
}
// 释放配置内存
int freeConfig(ConfigT *pconfig) {
if (pconfig == NULL) return -1;
if (pconfig->Mqtt.Organization != NULL) free(pconfig->Mqtt.Organization);
if (pconfig->Mqtt.SUBTOPIC != NULL) free(pconfig->Mqtt.SUBTOPIC);
if (pconfig->Mqtt.PUBTOPIC != NULL) free(pconfig->Mqtt.PUBTOPIC);
if (pconfig->Mqtt.SERVERADDRESS != NULL) free(pconfig->Mqtt.SERVERADDRESS);
if (pconfig->Mqtt.CLIENTID != NULL) free(pconfig->Mqtt.CLIENTID);
if (pconfig->Log.LogLevel != NULL) free(pconfig->Log.LogLevel);
if (pconfig->Log.LogFilePath != NULL) free(pconfig->Log.LogFilePath);
if (pconfig->Wifi.SSID != NULL) free(pconfig->Wifi.SSID);
if (pconfig->Wifi.Password != NULL) free(pconfig->Wifi.Password);
for (int i = 0; i < pconfig->BLE.ScanPrefix.Count; i++) {
if (pconfig->BLE.ScanPrefix.Items[i] != NULL) free(pconfig->BLE.ScanPrefix.Items[i]);
}
if (pconfig->BLE.ScanPrefix.Items != NULL) free(pconfig->BLE.ScanPrefix.Items);
if (pconfig->PDU.Addr != NULL) free(pconfig->PDU.Addr);
if (pconfig->PDU.Parity != NULL) free(pconfig->PDU.Parity);
if (pconfig->RealPower.Addr != NULL) free(pconfig->RealPower.Addr);
if (pconfig->RealPower.Parity != NULL) free(pconfig->RealPower.Parity);
if (pconfig->WebRTC.SignalingServer != NULL) free(pconfig->WebRTC.SignalingServer);
if (pconfig->WebRTC.ICEServer != NULL) free(pconfig->WebRTC.ICEServer);
for (int i = 0; i < pconfig->IPList.Count; i++) {
if (pconfig->IPList.Items[i] != NULL) free(pconfig->IPList.Items[i]);
}
if (pconfig->IPList.Items != NULL) free(pconfig->IPList.Items);
return 0;
}
// 设置默认配置
void setDefaultConfig(ConfigT *pconfig) {
if (pconfig == NULL) return;
// MQTT 配置默认值
if (pconfig->Mqtt.SUBTOPIC == NULL) pconfig->Mqtt.SUBTOPIC = strdup("default_topic");
if (pconfig->Mqtt.PUBTOPIC == NULL) pconfig->Mqtt.PUBTOPIC = strdup("default_topic");
if (pconfig->Mqtt.SERVERADDRESS == NULL) pconfig->Mqtt.SERVERADDRESS = strdup("tcp://localhost:1883");
if (pconfig->Mqtt.CLIENTID == NULL) pconfig->Mqtt.CLIENTID = strdup("default_client");
if (pconfig->Mqtt.QOS == 0) pconfig->Mqtt.QOS = 1;
if (pconfig->Mqtt.HEARTTIME == 0) pconfig->Mqtt.HEARTTIME = 60;
// 日志配置默认值
if (pconfig->Log.LogLevel == NULL) pconfig->Log.LogLevel = strdup("info");
if (pconfig->Log.LogFilePath == NULL) pconfig->Log.LogFilePath = strdup("/var/log/kvmagent.log");
if (pconfig->Log.LogMaxSize == 0) pconfig->Log.LogMaxSize = 10485760; // 10MB
// WiFi 配置默认值
if (pconfig->Wifi.SSID == NULL) pconfig->Wifi.SSID = strdup("default_ssid");
if (pconfig->Wifi.Password == NULL) pconfig->Wifi.Password = strdup("default_password");
if (pconfig->Wifi.RetryInterval == 0) pconfig->Wifi.RetryInterval = 10; // 10 秒
// BLE 配置默认值
// pconfig->BLE.ScanPrefix = NULL;
// pconfig->BLE.ScanPrefixCount = 0;
// if (pconfig->BLE. == 0) pconfig->BLE.ScanInterval = 10; // 10 秒
if (pconfig->BLE.ScanInterval == 0) pconfig->BLE.ScanInterval = 0x10; // 10 毫秒
if (pconfig->BLE.ScanWindow == 0) pconfig->BLE.ScanWindow = 0x10; // 10 毫秒
if (pconfig->BLE.AValue == 0) pconfig->BLE.AValue = 63.0; // 63.0 dBm
if (pconfig->BLE.NValue == 0) pconfig->BLE.NValue = 5.0; // 2.0
// if (pconfig->BLE.ScanTimeout == 0) pconfig->BLE.ScanTimeout = 30; // 30 秒
// PDU 配置默认值
if (pconfig->PDU.Addr == NULL) pconfig->PDU.Addr = strdup("/dev/ttyS3");
if (pconfig->PDU.Port == 0) pconfig->PDU.Port = 502;
if (pconfig->PDU.CollectInterval == 0) pconfig->PDU.CollectInterval = 60; // 60 秒
// WebRTC 配置默认值
if (pconfig->WebRTC.SignalingServer == NULL) pconfig->WebRTC.SignalingServer = strdup("ws://localhost:8080");
if (pconfig->WebRTC.ICEServer == NULL) pconfig->WebRTC.ICEServer = strdup("stun:stun.l.google.com:19302");
// IP 列表默认值
// if (pconfig->IPList == NULL) {
// for (int i = 0; i < pconfig->IPList.Count; i++) {
// if (pconfig->IPList.Items[i] != NULL) free(pconfig->IPList.Items[i]);
// }
// free(pconfig->IPList.Items);
// }
// pconfig->IPList.Items = NULL;
// pconfig->IPList.Count = 0;
}
// 验证配置
int validateConfig(ConfigT *pconfig) {
if (pconfig == NULL) return -1;
if (pconfig->Mqtt.SERVERADDRESS == NULL || strlen(pconfig->Mqtt.SERVERADDRESS) == 0) {
syslog(LOG_ERR, "serveraddress is not set");
// return -1;
}
if (pconfig->Mqtt.CLIENTID == NULL || strlen(pconfig->Mqtt.CLIENTID) == 0) {
syslog(LOG_ERR, "clientid is not set");
// return -1;
}
if (pconfig->Wifi.SSID == NULL || strlen(pconfig->Wifi.SSID) == 0) {
syslog(LOG_ERR, "WiFi SSID is not set");
// return -1;
}
return 0;
}
void getvalue(yaml_event_t event, pKeyValue *ppconfigs) {
if (event.type != YAML_SCALAR_EVENT) return; // 仅处理标量事件
char *value = (char *)event.data.scalar.value;
pKeyValue pconfig = *ppconfigs;
while (pconfig->key != NULL) {
// 处理列表项
if (parse_state.in_sequence && parse_state.current_list != NULL) {
parse_state.current_list->Items = realloc(parse_state.current_list->Items,
(parse_state.current_list->Count + 1) * sizeof(char *));
parse_state.current_list->Items[parse_state.current_list->Count] = strdup(value);
parse_state.current_list->Count++;
// parse_state.in_sequence = false;
// parse_state.current_list = NULL;
memset(parse_state.current_key, 0, sizeof(parse_state.current_key));
return; // 列表项无需进一步匹配键
}
if((strcmp(value,pconfig->key)==0)&&(pconfig->valuetype == STRUCT_TYPE_NAME || pconfig->valuetype == MAP_TYPE_NAME)) {
strncpy(parse_state.current_parent, pconfig->key, sizeof(parse_state.current_parent) - 1);
parse_state.current_parent[sizeof(parse_state.current_parent) - 1] = '\0';
return;
}
// 处理嵌套结构
if (strlen(parse_state.current_parent) > 0) {
// 检查配置项的父键是否匹配当前上下文
if (strcmp(parse_state.current_parent, pconfig->key) == 0) {
do{
if(strlen(parse_state.current_key)==0){
if(strcmp(value,pconfig->key)==0){
strncpy(parse_state.current_key, value, sizeof(parse_state.current_key) - 1);
parse_state.current_key[sizeof(parse_state.current_key) - 1] = '\0';
return;
}
}else if(strcmp(parse_state.current_key,pconfig->key)==0){
break;
}
pconfig++;
if(pconfig->key==NULL) return;
}while(true);
switch (pconfig->valuetype) {
case STRUCT_TYPE_NAME:
// 进入子结构体,更新当前父键
strncpy(parse_state.current_key, value, sizeof(parse_state.current_key) - 1);
parse_state.current_key[sizeof(parse_state.current_key) - 1] = '\0';
break;
case LIST_TYPE_NAME:
// 标记进入列表,后续标量将作为列表项处理
parse_state.in_sequence = true;
parse_state.current_list = (ListConfig *)pconfig->value;
continue;
default:
// 处理标量值(字符串、整数等)
if (pconfig->valuetype == STRING_TYPE_NAME) {
*((char **)pconfig->value) = strdup(value);
} else if (pconfig->valuetype == INT_TYPE_NAME) {
*((int *)pconfig->value) = atoi(value);
} else if (pconfig->valuetype == FLOAT_TYPE_NAME) {
*((float *)pconfig->value) = atof(value);
}else if (pconfig->valuetype == BOOL_TYPE_NAME) {
*((bool *)pconfig->value) = (strcasecmp(value, "true") == 0);
}
// 重置父键(退出当前结构体)
memset(parse_state.current_key, 0, sizeof(parse_state.current_key));
break;
}
return;
}
} else {
// 处理新键值
if (strcmp(value, pconfig->key) == 0) {
if (pconfig->valuetype == LIST_TYPE_NAME) {
parse_state.in_sequence = true;
parse_state.current_list = (ListConfig *)pconfig->value;
}else{
// 进入结构体
strncpy(parse_state.current_parent, pconfig->key, sizeof(parse_state.current_parent) - 1);
parse_state.current_parent[sizeof(parse_state.current_parent) - 1] = '\0';
}
return;
}
}
pconfig++;
}
// 未匹配的键,记录警告
syslog(LOG_WARNING, "Unknown key '%s' under parent '%s'", value, parse_state.current_parent);
}
int Load_YAML_Config(char *yaml_file, KeyValue *(configs[])) {
FILE *fh = fopen(yaml_file, "r");
yaml_parser_t parser;
yaml_event_t event;
bool done = false;
// 初始化解析状态
memset(&parse_state, 0, sizeof(parse_state));
yaml_parser_initialize(&parser);
yaml_parser_set_input_file(&parser, fh);
while (!done) {
if (!yaml_parser_parse(&parser, &event)) {
syslog(LOG_ERR, "Parser error: %s", parser.problem);
break;
}
switch (event.type) {
case YAML_MAPPING_START_EVENT:
// 映射开始,无需操作,由 getvalue 处理
break;
case YAML_MAPPING_END_EVENT:
// 映射结束,退出当前父键
memset(parse_state.current_parent, 0, sizeof(parse_state.current_parent));
break;
case YAML_SEQUENCE_START_EVENT:
parse_state.in_sequence = true;
break;
case YAML_SEQUENCE_END_EVENT:
parse_state.in_sequence = false;
parse_state.current_list = NULL;
break;
case YAML_SCALAR_EVENT:
getvalue(event, configs);
break;
case YAML_STREAM_END_EVENT:
done = true;
break;
default:
break;
}
yaml_event_delete(&event);
}
yaml_parser_delete(&parser);
fclose(fh);
return 0;
}
// 初始化配置
int InitConfig() {
openlog("kvmagent", LOG_PID | LOG_CONS, LOG_USER);
pKeyValue pconfig = &webrtcconfig[0];
char cwd[256];
if (getcwd(cwd, sizeof(cwd)) != NULL) {
syslog(LOG_INFO, "Current working directory: %s", cwd);
} else {
syslog(LOG_ERR, "Failed to get current working directory");
return -1;
}
char configfilepath[512];
// snprintf(configfilepath,512 ,"%s/rv1106/lvgl_medical/lib/webrtc/kvsRTC-mqtt/yamlconfig/config/config.yaml", cwd);
snprintf(configfilepath,512 ,"%s/config/config.yaml", cwd);
printf("configfilepath:%s\n",configfilepath);
memset(&globalConfig, 0, sizeof(globalConfig));
Load_YAML_Config(configfilepath, &pconfig);
setDefaultConfig(&globalConfig);
if (validateConfig(&globalConfig) != 0) {
syslog(LOG_ERR, "Configuration validation failed");
return -1;
}
printConfig(&globalConfig);
return 0;
}
void CloseConfig(){
closelog();
freeConfig(&globalConfig);
}
ConfigT *GetConfig(){
return &globalConfig;
}
// // // // 主函数
// int main(int argc, char *argv[]) {
// openlog("kvmagent", LOG_PID | LOG_CONS, LOG_USER);
// if (InitConfig() != 0) {
// syslog(LOG_ERR, "Failed to initialize configuration");
// closelog();
// CloseConfig();
// return 1;
// }
// closelog();
// CloseConfig();
// return 0;
// }
后记,在技术日新月异的今天,要跟上发展,程序员实属不易,如何沉下心来做产品,除了需要极大的耐心外,更需要在眼花缭乱的新技术面前保持平衡,不落伍也不冒进,这是大智慧,需要终身修行和实践。