代码优化
上面设备类的代码都是重复设备信息配置, 因此选择非常的冗余,其实这些信息完全可以利用配置文件
进行配置,这样就不需要如此多的设备类节点代码, 也方便后期的添加维护。
一、设备类节点直接通过文件配置
- 什么是.ini文件
ini文件通常以纯文本形式存在,并且包含了一个或多个节(sections)以及每个节下的键值对(keyvalue pairs)。这些键值对用来指定应用程序的各种设置。
比如Linux系统里就有非常多这类格式的文件,如Linux下的打印机服务程序启动配置文件/lib/systemd/system/cups.service
:
[Unit]
Description=CUPS Scheduler
Documentation=man:cupsd(8)
After=network.target nss-user-lookup.target nslcd.service
Requires=cups.socket
[Service]
ExecStart=/usr/sbin/cupsd -l
Type=notify
Restart=on-failure
[Install]
Also=cups.socket cups.path
WantedBy=printer.target multi-user.target
- inih解析库介绍
inih是一个轻量级的C库,用于解析INI格式的配置文件。这个库由Ben Hoyt开发,并在GitHub上提供源
代码(https://github.com/benhoyt/inih)。 inih 库的设计目标是简单易用,同时保持最小的依赖性。
以下是关于inih库的一些特点:
跨平台:inih库是跨平台的,可以在多种操作系统和编译器环境下使用。
体积小:inih库只有几个C文件,非常适合嵌入到其他项目中。
可定制:用户可以通过回调函数来处理读取到的键值对,使得处理方式非常灵活。
易于集成:只需要将ini.c和ini.h两个文件添加到你的项目中即可开始使用。
支持注释:inih库可以正确地处理以分号或哈希字符开头的行作为注释。
错误处理:如果在解析过程中遇到错误,ini_parse()函数会返回一个负数。
要使用inih库,你需要在你的代码中包含ini.h头文件,并调用ini_parse()函数来解析INI文件。ini_parse()函数接受三个参数:要解析的文件名、一个回调函数以及一个用户数据指针。每当找到一个新的键值对时,都会调用回调函数。
例如,以下是一个简单的回调函数示例:
static int handler(void* user, const char* section, const char* name, const char* value)
{
printf("Section: '%s', Name: '%s', Value: '%s'\n", section, name, value);
return 1; /* 成功 */
}
然后,你可以像这样调用ini_parse()函数:
int error = ini_parse("config.ini", handler, NULL);
if (error < 0) {
printf("Can't load 'config.ini'\n");
exit(1);
}
如果你需要更复杂的处理逻辑,你可以在回调函数中实现它。注意,inih库并不直接提供设置的持久化功能,因此你需要自己负责将修改后的设置写回INI文件
- 首先定义设备控制ini文件gdevice.ini
[lock]
key=0x44
gpio_pin=8
gpio_mode=OUTPUT
gpio_status=HIGH
check_face_status=1
voice_set_status=1
[beep]
key=0x45
gpio_pin=9
gpio_mode=OUTPUT
gpio_status=HIGH
check_face_status=0
voice_set_status=1
[BR led]
key=0x42
gpio_pin=5
gpio_mode=OUTPUT
gpio_status=HIGH
check_face_status=0
voice_set_status=0
[LV led]
key=0x41
gpio_pin=2
gpio_mode=OUTPUT
gpio_status=HIGH
check_face_status=0
voice_set_status=0
[fan]
key=0x43
gpio_pin=7
gpio_mode=OUTPUT
gpio_status=HIGH
check_face_status=0
voice_set_status=0
- 下载libinih1源代码:
apt source libinih1
会下载libinih1d源码, 将libinih-53目录中的ini.c
和ini.h
拷贝到项目工程中,同时移除设备类信息文件
pg@pg-Default-string:~/libinih-53$ cp ini.h ../smarthome/inc/
pg@pg-Default-string:~/libinih-53$ cp ini.c ../smarthome/src/
pg@pg-Default-string:~/smarthome$ rm -rf src/*_gdevice.c inc/*_gdevice.h
目录结构如下:
pg@pg-Default-string:~/smarthome$ ls
3rd inc ini Makefile src
pg@pg-Default-string:~/smarthome$ tree -I 3rd
.
├── inc
│ ├── control.h
│ ├── face.h
│ ├── gdevice.h
│ ├── global.h
│ ├── ini.h
│ ├── msg_queue.h
│ ├── myoled.h
│ ├── receive_interface.h
│ ├── smoke_interface.h
│ ├── socket.h
│ ├── socket_interface.h
│ ├── uartTool.h
│ └── voice_interface.h
├── ini
│ └── gdevice.ini
├── Makefile
└── src
├── control.c
├── face.c
├── face.py
├── gdeive.c
├── ini.c
├── main.c
├── msg_queue.c
├── myoled.c
├── receive_interface.c
├── smoke_interface.c
├── socket.c
├── socket_interface.c
├── uartTool.c
└── voice_interface.c
二、接收处理代码重新实现
修改receive_interface.c代码,实现利用libinih1解析库
解析ini文件获取设备类节点:
#include <pthread.h>
#include <mqueue.h>
#include <stdlib.h>
#include <stdio.h>
#include "wiringPi.h"
#include "control.h"
#include "receive_interface.h"
#include "msg_queue.h"
#include "global.h"
#include "face.h"
#include "myoled.h"
#include "ini.h"
#include "gdevice.h"
typedef struct {
int msg_len;
unsigned char *buffer;
ctrl_info_t *ctrl_info;
}recv_msg_t;
static int oled_fd = -1;
static struct gdevice *pdevhead = NULL;
#define MATCH(s, n) strcmp(section, s) == 0 && strcmp(name, n) == 0
static int handler_gdevice(void* user, const char* section, const char* name, const char* value)
{
struct gdevice *pdev = NULL;
if (NULL == pdevhead)
{
pdevhead = (struct gdevice *)malloc(sizeof(struct gdevice));
pdevhead->next = NULL;
memset(pdevhead, 0, sizeof(struct gdevice));
strcpy(pdevhead->dev_name, section);
}
else if (0 != strcmp(section, pdevhead->dev_name))
{
pdev = (struct gdevice *)malloc(sizeof(struct gdevice));
memset(pdev, 0, sizeof(struct gdevice));
strcpy(pdev->dev_name, section);
pdev->next = pdevhead;
pdevhead = pdev;
}
if (NULL != pdevhead)
{
if(MATCH(pdevhead->dev_name, "key"))
{
sscanf(value, "%x", &pdevhead->key);
printf("%d|pdevhead->key=%x\n",__LINE__, pdevhead->key);
}
else if(MATCH(pdevhead->dev_name, "gpio_pin"))
{
pdevhead->gpio_pin = atoi(value);
}
else if(MATCH(pdevhead->dev_name, "gpio_mode"))
{
if(strcmp(value, "OUTPUT") == 0)
{
pdevhead->gpio_mode = OUTPUT; //OUTPUT
}
else if (strcmp(value, "INPUT") == 0)
{
pdevhead->gpio_mode = INPUT;
}
}
else if(MATCH(pdevhead->dev_name, "gpio_status"))
{
if(strcmp(value, "LOW") == 0)
{
pdevhead->gpio_mode = LOW; //OUTPUT
}
else if (strcmp(value, "HIGH") == 0)
{
pdevhead->gpio_mode = HIGH;
}
}
else if(MATCH(pdevhead->dev_name, "check_face_status"))
{
pdevhead->check_face_status = atoi(value);
}
else if(MATCH(pdevhead->dev_name, "voice_set_status"))
{
pdevhead->voice_set_status = atoi(value);
}
}
return 1;
}
static int receive_init(void)
{
if (ini_parse("/etc/gdevice.ini", handler_gdevice, NULL) < 0) {
printf("Can't load 'gdevice.ini'\n");
return 1;
}
oled_fd = myoled_init();
face_init();
return oled_fd;
}
static void receive_final(void)
{
face_final();
if(oled_fd != -1)
{
close(oled_fd);
oled_fd = -1;
}
}
static void *handle_device(void *arg)
{
recv_msg_t *recv_msg = NULL;
struct gdevice *cur_gdev = NULL;
char success_or_failed[20] = "success";
int ret = -1;
pthread_t tid = -1;
int smoke_status = 0;
double face_result = 0.0;
pthread_detach(pthread_self());
//do something
if (NULL != arg)
{
recv_msg = (recv_msg_t *)arg;
printf("recv_msg->msg_len = %d\n", recv_msg->msg_len);
printf("%s|%s|%d:hanle 0x%x, 0x%x,0x%x, 0x%x, 0x%x,0x%x\n", __FILE__, __func__, __LINE__, recv_msg->buffer[0], recv_msg->buffer[1], recv_msg->buffer[2], recv_msg->buffer[3], recv_msg->buffer[4],recv_msg->buffer[5]);
}
if (NULL != recv_msg && NULL != recv_msg->buffer)
{
cur_gdev = find_device_by_key(pdevhead, recv_msg->buffer[2]);
}
if (NULL != cur_gdev)
{
cur_gdev->gpio_status = recv_msg->buffer[3] == 0 ? LOW : HIGH;
//special for lock
printf("%s|%s|%d:cur_gdev->check_face_status=%d\n", __FILE__, __func__, __LINE__, cur_gdev->check_face_status);
if (1 == cur_gdev->check_face_status)
{
face_result = face_category();
printf("%s|%s|%d:face_result=%f\n", __FILE__, __func__, __LINE__, face_result);
if (face_result > 0.6)
{
ret = set_gpio_gdevice_status(cur_gdev);
recv_msg->buffer[2] = 0x47;
}
else
{
recv_msg->buffer[2] = 0x46;
ret = -1;
}
}
else if (0 == cur_gdev->check_face_status)
{
ret = set_gpio_gdevice_status(cur_gdev);
}
if(1 == cur_gdev->voice_set_status)
{
if (NULL != recv_msg && NULL != recv_msg->ctrl_info && NULL != recv_msg->ctrl_info->ctrl_phead)
{
struct control *pcontrol = recv_msg->ctrl_info->ctrl_phead;
while (NULL != pcontrol)
{
if (strstr(pcontrol->control_name, "voice"))
{
if (0x45 == recv_msg->buffer[2] && 0 == recv_msg->buffer[3])
{
smoke_status = 1;
}
pthread_create(&tid, NULL, pcontrol->set, (void*)recv_msg->buffer);
break;
}
pcontrol = pcontrol->next;
}
}
}
if (-1 == ret)
{
memset(success_or_failed, '\0', sizeof(success_or_failed));
strncpy(success_or_failed, "failed", 6);
}
//oled屏显示
char oled_msg[512];
memset(oled_msg, 0, sizeof(oled_msg));
char *change_status = cur_gdev->gpio_status == LOW ? "Open" : "Close";
sprintf(oled_msg, "%s %s %s!\n", change_status, cur_gdev->dev_name, success_or_failed);
//special for smoke
if(smoke_status == 1)
{
memset(oled_msg, 0, sizeof(oled_msg));
strcpy(oled_msg, "A risk of fire!\n");
}
printf("oled_msg=%s\n", oled_msg);
oled_show(oled_msg);
//special for lock, close lock
if (1 == cur_gdev->check_face_status && 0 == ret && face_result > 0.6)
{
sleep(5);
cur_gdev->gpio_status = HIGH;
set_gpio_gdevice_status(cur_gdev);
}
}
pthread_exit(0);
}
static void* receive_get(void *arg)
{
recv_msg_t *recv_msg = NULL;
ssize_t read_len = -1;
pthread_t tid = -1;
char *buffer = NULL;
struct mq_attr attr;
if (NULL != arg)
{
recv_msg = (recv_msg_t *)malloc(sizeof(recv_msg_t));
recv_msg->ctrl_info = (ctrl_info_t *)arg; //获取到mqd 和phead (struct control 链表的头结点)
recv_msg->msg_len = -1;
recv_msg->buffer = NULL;
}
else
{
pthread_exit(0);
}
if(mq_getattr(recv_msg->ctrl_info->mqd, &attr) == -1)
{
pthread_exit(0);
}
recv_msg->buffer = (unsigned char *)malloc(attr.mq_msgsize);
buffer = (unsigned char *)malloc(attr.mq_msgsize);
memset(recv_msg->buffer, 0, attr.mq_msgsize);
memset(buffer, 0, attr.mq_msgsize);
pthread_detach(pthread_self());
struct timespec timeout = {
.tv_sec = 5, .tv_nsec = 0 };
while(1)
{
// read_len = mq_receive(recv_msg->ctrl_info->mqd, buffer, attr.mq_msgsize, NULL);
read_len = mq_timedreceive(recv_msg->ctrl_info->mqd, buffer, attr.mq_msgsize, NULL, &timeout);
printf("%s|%s|%d:send 0x%x, 0x%x,0x%x, 0x%x, 0x%x,0x%x\n", __FILE__, __func__, __LINE__, buffer[0], buffer[1], buffer[2], buffer[3], buffer[4],buffer[5]);
printf("%s|%s|%d:read_len=%ld\n", __FILE__, __func__, __LINE__, read_len);
if (-1 == read_len)
{
if(errno == EAGAIN)
{
printf("queue is empyt\n");
}
else if (errno == ETIMEDOUT) {
printf("timeout\n");
continue;
}
else
{
break;
}
}
else if(buffer[0] == 0xAA && buffer[1] == 0x55
&& buffer[5] == 0xAA && buffer[4] == 0x55)
{
recv_msg->msg_len = read_len;
memcpy(recv_msg->buffer, buffer, read_len);
pthread_create(&(tid), NULL, handle_device, (void *)recv_msg);
}
}
pthread_exit(0);
}
struct control receive_control = {
.control_name = "receive",
.init = receive_init,
.final = receive_final,
.get = receive_get,
.set = NULL,
.next = NULL
};
struct control *add_receive_to_ctrl_list(struct control *phead)
{
//头插法
return add_interface_to_ctrl_list(phead, &receive_control);
};