【OrangePi Zero2 智能家居】智能家居项目的软件实现

一、项目整体设计
二、项目代码的前期准备
三、实现语音监听接口
四、实现socket监听接口
五、实现烟雾报警监听接口
六、实现设备节点代码
七、实现接收消息处理接口

一、项目整体设计

整体的软件框架大致如下:
在这里插入图片描述
整个项目开启4个监听线程, 分别是:

  1. 语音监听线程:用于监听语音指令, 当有语音指令过来后, 通过消息队列的方式给消息处理线程发
    送指令
  2. 网络监听线程:用于监听网络指令,当有网络指令过来后, 通过消息队列的方式给消息处理线程发
    送指令
  3. 火灾检测线程:当存在煤气泄漏或者火灾闲情时, 发送警报指令给消息处理线程
  4. 消息监听线程: 用于处理以上3个线程发过来的指令,并根据指令要求配置GPIO引脚状态,OLED
    屏显示、语音播报,还有人脸识别开门
    上述四个线程采用统一个对外接口接口,同时添加到监听链表中。

统一的监听模块接口如下:

struct control
{char control_name[128]; //监听模块名称int (*init)(void); //初始化函数void (*final)(void);//结束释放函数void *(*get)(void *arg);//监听函数,如语音监听void *(*set)(void *arg); //设置函数,如语音播报struct control *next;
};struct control *add_device_to_ctrl_list(struct control *phead, struct control *device);

另外,被控制的设备类也统一配置接口,同时添加到设备链表中。

统一的设备类接口如下:

struct gdevice
{char dev_name[128]; //设备名称int key; //key值,用于匹配控制指令的值int gpio_pin; //控制的gpio引脚int gpio_mode; //输入输出模式int gpio_status; //高低电平状态int check_face_status; //是否进行人脸检测状态int voice_set_status; //是否语音语音播报struct gdevice *next;
};struct gdevice *add_device_to_gdevice_list(struct gdevice *phead, struct gdevice *device);
struct gdevice *find_gdevice_by_key(struct gdevice *pdev, unsigned char key);
int set_gpio_gdevice_status(struct gdevice *pdev);

二、项目代码的前期准备

之前讲过智能分类的项目,因为会用到语音模块、OLED显示、网络模块、这些代码都可以从智能分类的项目中直接拷贝过来使用,另外添加之前准备好的人脸识别的代码 。 另外根据《项目整体设计》。再定义gdevice.h和control.h的头文件。整个目录结构如下:

pg@pg-Default-string:~/smarthome$ tree -I 3rd/ #3rd目录直接从garbage工程拷贝过来, 主要是一些依赖库和头文件, 这里就不显示
.
├── inc
│ ├── control.h
│ ├── face.h
│ ├── gdevice.h
│ ├── myoled.h
│ ├── socket.h
│ └── uartTool.h
├── Makefile
└── src├── face.c├── face.py├── myoled.c├── socket.c└── uartTool.c

其中 control.h代码如下:

#ifndef __CONTROL__H
#define __CONTROL__H
#include <stdlib.h>struct control
{char control_name[128];int (*init)(void);void (*final)(void);void *(*get)(void *arg);void *(*set)(void *arg);struct control *next;
};//头插法,用于control类链表的创建
struct control *add_device_to_ctrl_list(struct control *phead, struct control *device);
#endif// /dev/ttyS5 115200 ip port buffer pin /dev/I2C-3

control.c 代码如下:

#include "control.h"
//头插法
struct control *add_device_to_ctrl_list(struct control *phead, struct control *device)
{struct control *pcontrol;if(NULL == phead){pcontrol = device;return pcontrol;}else{device->next = phead;phead = device;return phead;}
}

gdevice.h 代码如下:

#ifndef __GDEVICE_H
#define __GDEVICE_Hstruct gdevice
{char dev_name[128]; //设备名称int key; //key值,用于匹配控制指令的值int gpio_pin; //控制的gpio引脚int gpio_mode; //输入输出模式int gpio_status; //高低电平状态int check_face_status; //是否进行人脸检测状态int voice_set_status; //是否语音语音播报struct gdevice *next;
};
#endif

gdevice.c 代码如下:

#include <wiringPi.h>
#include "gdevice.h"//根据key值(buffer[2])查找设备节点
struct gdevice *find_gdevice_by_key(struct gdevice *pdev, unsigned char key)
{struct gdevice *p = NULL;if (NULL == pdev){return NULL;}p = pdev;while (NULL != p){if(p->key == key){return p;}p = p->next;}return NULL;
}//设置GPIO引脚状态,输入输出和高低电平
int set_gpio_gdevice_status(struct gdevice *pdev)
{if (NULL == pdev){return -1;}if (-1 != pdev->gpio_pin){if (-1 != pdev->gpio_mode){pinMode(pdev->gpio_pin, pdev->gpio_mode);}if (-1 != pdev->gpio_status){digitalWrite(pdev->gpio_pin, pdev->gpio_status);}}return 0;
}//链表头插法
struct gdevice *add_device_to_gdevice_list(struct gdevice *phead, struct gdevice *device)
{struct gdevice *pgdevice;if(NULL == phead){pgdevice = device;return pgdevice;}else{device->next = phead;phead = device;return phead;}
}

Makefile 修改后内容如下:

CC := aarch64-linux-gnu-gcc
SRC := $(shell find src -name "*.c")
INC := ./inc \./3rd/usr/local/include \./3rd/usr/include \./3rd/usr/include/python3.10 \./3rd/usr/include/aarch64-linux-gnu/python3.10 \./3rd/usr/include/aarch64-linux-gnuOBJ := $(subst src/,obj/,$(SRC:.c=.o))TARGET=obj/smarthomeCFLAGS := $(foreach item, $(INC),-I$(item)) # -I./inc -I./3rd/usr/local/include
LIBS_PATH := ./3rd/usr/local/lib \./3rd/lib/aarch64-linux-gnu \./3rd/usr/lib/aarch64-linux-gnu \./3rd/usr/lib/python3.10 \#LLDFLAGS := $(foreach item, $(LIBS_PATH),-L$(item)) # -L./3rd/usr/local/libs
LIBS := -lwiringPi -lpython3.10 -pthread -lexpat -lz -lcrypt
obj/%.o:src/%.cmkdir -p obj$(CC) -o $@ -c $< $(CFLAGS)$(TARGET) :$(OBJ)$(CC) -o $@ $^ $(CFLAGS) $(LDFLAGS) $(LIBS)compile : $(TARGET)clean:rm $(TARGET) obj $(OBJ) -rfdebug:echo $(CC)echo $(SRC)echo $(INC)echo $(OBJ)echo $(TARGET)echo $(CFLAGS)echo $(LDFLAGS)echo $(LIBS).PHONY: clean compile debug

三、实现语音监听接口

语音监听模块会借助消息队列进行消息的传递,因此先实现消息队列的接口 msg_queque.c:

#include <stdio.h>
#include "msg_queue.h"#define QUEQUE_NAME "/mq_queue"mqd_t msg_queue_create(void)
{//创建消息队列mqd_t mqd = -1;struct mq_attr attr;attr.mq_flags = 0;attr.mq_maxmsg = 10;attr.mq_msgsize = 256;attr.mq_curmsgs = 0;mqd = mq_open(QUEQUE_NAME, O_CREAT | O_RDWR, 0666, &attr);printf("%s| %s |%d: mqd = %d\n",__FILE__, __func__, __LINE__, mqd);return mqd;
}void msg_queue_final(mqd_t mqd)
{if (-1 != mqd){mq_close(mqd);mq_unlink(QUEQUE_NAME);mqd = -1;}
}int send_message(mqd_t mqd, void *msg, int msg_len)
{int byte_send = -1;byte_send = mq_send(mqd, (char *)msg, msg_len, 0);return byte_send;
}

msg_queue.h 头文件定义:

#ifndef __MSG_QUEQUE_H
#define __MSG_QUEQUE_H#include <mqueue.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>mqd_t msg_queue_create(void);
void msg_queue_final(mqd_t mqd);
int send_message(mqd_t mqd, void *msg, int msg_len);#endif

根据control.h头文件的定义,实现语音监听接口

首先定义全局变量用于mqd句柄和struct control链表的传递 global.h 代码

#ifndef __GLOBAL__H
#define __GLOBAL__Htypedef struct {mqd_t mqd;struct control *ctrl_phead;
}ctrl_info_t;
#endif

紧接着语音监听接口 voice_interface.c 代码

#if 0
struct control
{char control_name[128]; //监听模块名称int (*init)(void); //初始化函数void (*final)(void);//结束释放函数void *(*get)(void *arg);//监听函数,如语音监听void *(*set)(void *arg); //设置函数,如语音播报struct control *next;
};
#endif
#include <pthread.h>
#include <stdio.h>#include "voice_interface.h"
#include "uartTool.h"
#include "msg_queue.h"
#include "global.h"static int serial_fd = -1;static int voice_init(void)
{serial_fd = myserialOpen (SERIAL_DEV, BAUD);printf("%s|%s|%d:serial_fd=%d\n", __FILE__, __func__, __LINE__, serial_fd);return serial_fd;
}static void voice_final(void)
{if (-1 != serial_fd){close(serial_fd);serial_fd = -1;}
}//接收语音指令
static void *voice_get(void *arg) // mqd应该来自于arg传参
{unsigned char buffer[6] = {0x00, 0x00, 0x00, 0x00, 0X00, 0x00};int len = 0;mqd_t mqd = -1;ctrl_info_t *ctrl_info= NULL;if (NULL != arg)ctrl_info = (ctrl_info_t *)arg;if (-1 == serial_fd){serial_fd = voice_init();if (-1 == serial_fd){pthread_exit(0);}}if(NULL != ctrl_info){mqd = ctrl_info->mqd;}if ((mqd_t)-1 == mqd){pthread_exit(0);}pthread_detach(pthread_self());printf("%s thread start\n", __func__);while(1){len = serialGetstring(serial_fd, buffer);printf("%s|%s|%d: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:len=%d\n", __FILE__, __func__, __LINE__, len);if (len > 0){if(buffer[0] == 0xAA && buffer[1] == 0x55&& buffer[5] == 0xAA && buffer[4] == 0x55){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]);send_message(mqd, buffer, len);//注意,不要用strlen去计算实际的长度}memset(buffer, 0, sizeof(buffer));}}pthread_exit(0);
}//语音播报
static void *voice_set(void *arg)
{pthread_detach(pthread_self());unsigned char *buffer = (unsigned char *)arg;if (-1 == serial_fd){serial_fd = voice_init();if (-1 == serial_fd){pthread_exit(0);}}if (NULL != buffer){serialSendstring(serial_fd, buffer, 6);}pthread_exit(0);
}struct control voice_control = {.control_name = "voice",.init = voice_init,.final = voice_final,.get = voice_get,.set = voice_set,.next = NULL
};struct control *add_voice_to_ctrl_list(struct control *phead)
{//头插法return add_interface_to_ctrl_list(phead, &voice_control);
};

voice_interface.h代码

#ifndef ___VOICE_INTERFACE_H___
#define ___VOICE_INTERFACE_H___#include "control.h"struct control *add_voice_to_ctrl_list(struct control *phead);#endif

四、实现socket监听接口

参考voice接口实现socket 接口socket_interface.c代码

#include <pthread.h>#include "socket.h"
#include "control.h"
#include "socket_interface.h"
#include "msg_queue.h"
#include "global.h"static int s_fd = -1;static int tcpsocket_init(void)
{s_fd = socket_init(IPADDR, IPPORT);return -1;
}static void tcpsocket_final(void)
{close(s_fd);s_fd = -1;
}static void* tcpsocket_get(void *arg)
{int c_fd = -1;int ret = -1;struct sockaddr_in c_addr;unsigned char buffer[BUF_SIZE];mqd_t mqd = -1;ctrl_info_t *ctrl_info= NULL;int keepalive = 1; // 开启TCP_KEEPALIVE选项int keepidle = 10; // 设置探测时间间隔为10秒int keepinterval = 5; // 设置探测包发送间隔为5秒int keepcount = 3; // 设置探测包发送次数为3次pthread_detach(pthread_self());printf("%s|%s|%d: s_fd = %d\n", __FILE__, __func__, __LINE__,s_fd);if (-1 == s_fd){s_fd = tcpsocket_init();if (-1 == s_fd){printf("tcpsocket_init failed\n");pthread_exit(0);}}if (NULL != arg)ctrl_info = (ctrl_info_t *)arg;if(NULL != ctrl_info){mqd = ctrl_info->mqd;}if ((mqd_t)-1 == mqd){pthread_exit(0);}memset(&c_addr,0,sizeof(struct sockaddr_in));//4. acceptint clen = sizeof(struct sockaddr_in);printf("%s thread start\n", __func__);while (1){c_fd = accept(s_fd,(struct sockaddr *)&c_addr,&clen);if (-1 == c_fd){continue;}ret = setsockopt(c_fd, SOL_SOCKET, SO_KEEPALIVE, &keepalive, sizeof(keepalive)); // 设置TCP_KEEPALIVE选项if (ret == -1) { // 如果设置失败,打印错误信息并跳出循环perror("setsockopt");break;}ret = setsockopt(c_fd, IPPROTO_TCP, TCP_KEEPIDLE, &keepidle, sizeof(keepidle)); // 设置探测时间间隔选项if (ret == -1) { // 如果设置失败,打印错误信息并跳出循环perror("setsockopt");break;}ret = setsockopt(c_fd, IPPROTO_TCP, TCP_KEEPINTVL, &keepinterval,sizeof(keepinterval)); // 设置探测包发送间隔选项if (ret == -1) { // 如果设置失败,打印错误信息并跳出循环perror("setsockopt");break;}ret = setsockopt(c_fd, IPPROTO_TCP, TCP_KEEPCNT, &keepcount,sizeof(keepcount)); // 设置探测包发送次数选项if (ret == -1) { // 如果设置失败,打印错误信息并跳出循环perror("setsockopt");break;}printf("Accepted a connection from %s:%d\n", inet_ntoa(c_addr.sin_addr),ntohs(c_addr.sin_port)); // 打印客户端的IP地址和端口号while (1){memset(buffer, 0, BUF_SIZE);ret = recv(c_fd, buffer, BUF_SIZE, 0);printf("%s|%s|%d: 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]);if (ret > 0){if(buffer[0] == 0xAA && buffer[1] == 0x55&& buffer[5] == 0xAA && buffer[4] == 0x55){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]);send_message(mqd, buffer, ret);//注意,不要用strlen去计算实际的长度}}else if ( -1 == ret || 0 == ret){break;}}}pthread_exit(0);
}struct control tcpsocket_control = {.control_name = "tcpsocket",.init = tcpsocket_init,.final = tcpsocket_final,.get = tcpsocket_get,.set = NULL,.next = NULL
};struct control *add_tcpsocket_to_ctrl_list(struct control *phead)
{//头插法return add_interface_to_ctrl_list(phead, &tcpsocket_control);
};

socket.h 代码

#ifndef ___SOCKET_INTERFACE_H___
#define ___SOCKET_INTERFACE_H___#include "control.h"struct control *add_tcpsocket_to_ctrl_list(struct control *phead);#endif

五、实现烟雾报警监听接口

同样参考voice接口实现smoke 接口smoke_interface.c代码

#include <pthread.h>
#include <wiringPi.h>
#include <stdio.h>#include "control.h"
#include "smoke_interface.h"
#include "msg_queue.h"
#include "global.h"#define SMOKE_PIN 6
#define SMOKE_MODE INPUTstatic int smoke_init(void)
{printf("%s|%s|%d\n", __FILE__, __func__, __LINE__);pinMode(SMOKE_PIN, SMOKE_MODE);return 0;
}static void smoke_final(void)
{//do nothing;
}static void* smoke_get(void *arg)
{int status = HIGH;int switch_status = 0;unsigned char buffer[6] = {0xAA, 0x55, 0x00, 0x00, 0x55, 0xAA};ssize_t byte_send = -1;mqd_t mqd = -1;ctrl_info_t *ctrl_info = NULL;if (NULL != arg)ctrl_info = (ctrl_info_t *)arg;if(NULL != ctrl_info){mqd = ctrl_info->mqd;}if ((mqd_t)-1 == mqd){pthread_exit(0);}pthread_detach(pthread_self());printf("%s thread start\n", __func__);while(1){status = digitalRead(SMOKE_PIN);if (LOW == status){buffer[2] = 0x45;buffer[3] = 0x00;switch_status = 1;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]);byte_send = mq_send(mqd, buffer, 6, 0);if (-1 == byte_send){continue;}}else if (HIGH == status && 1 == switch_status){buffer[2] = 0x45;buffer[3] = 0x01;switch_status = 0;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]);byte_send = mq_send(mqd, buffer, 6, 0);if (-1 == byte_send){continue;}}sleep(5);}pthread_exit(0);
}struct control smoke_control = {.control_name = "smoke",.init = smoke_init,.final = smoke_final,.get = smoke_get,.set = NULL,.next = NULL
};struct control *add_smoke_to_ctrl_list(struct control *phead)
{//头插法return add_interface_to_ctrl_list(phead, &smoke_control);
};

smoke_interface.h 代码

#ifndef ___SMOKE_INTERFACE_H___
#define ___SMOKE_INTERFACE_H___#include "control.h"struct control *add_smoke_to_ctrl_list(struct control *phead);#endif

六、实现设备节点代码

  1. 客厅灯设备节点
    由于消息接收处理线程需要处理各设备类外设,因此先根据gdevice.h定义,实现客厅灯设备节点代码
    lrled_gdevice.c:
#include "gdevice.h"struct gdevice lrled_gdev = {.dev_name = "LV led",.key = 0x41,.gpio_pin = 2,.gpio_mode = OUTPUT,.gpio_status = HIGH,.check_face_status = 0,.voice_set_status = 0,
};struct gdevice *add_lrled_to_gdevice_list(struct gdevice *pgdevhead)
{//头插法return add_device_to_gdevice_list(pgdevhead, &lrled_gdev);
};

lrled_gdevice.h 代码如下:

#ifndef __LRLED_GDEVICE_H
#define __LRLED_GDEVICE_Hstruct gdevice *add_lrled_to_gdevice_list(struct gdevice *pgdevhead);#endif
  1. 卧室灯设备节点
    卧室灯设备节点代码bled_gdevice.c:
#include "gdevice.h"struct gdevice bled_gdev = {.dev_name = "BR led",.key = 0x42,.gpio_pin = 5,.gpio_mode = OUTPUT,.gpio_status = HIGH,.check_face_status = 0,.voice_set_status = 0,
};struct gdevice *add_bled_to_gdevice_list(struct gdevice *pgdevhead)
{//头插法return add_device_to_gdevice_list(pgdevhead, &bled_gdev);
};

bled_gdevice.h 代码如下:

#ifndef __BLED_GDEVICE_H
#define __BLED_GDEVICE_Hstruct gdevice *add_bled_to_gdevice_list(struct gdevice *pgdevhead);#endif
  1. 实现风扇设备节点代码
    实现风扇设备节点代码fan_gdevice.c:
#include "gdevice.h"struct gdevice gdevice_fan = {.dev_name = "fan",.key = 0x43,.gpio_pin = 7,.gpio_mode = OUTPUT,.gpio_status = LOW,.check_face_status = 0,.voice_set_status = 0,.next = NULL
};struct gdevice *add_fan_to_gdevice_list(struct gdevice *phead)
{return add_device_to_gdevice_list(phead, &gdevice_fan);
}

fan_gdevice.h 代码如下:

#ifndef __FAN_GDEVICE_H
#define __FAN_GDEVICE_Hstruct gdevice *add_fan_to_gdevice_list(struct gdevice *pgdevhead);#endif
  1. 蜂鸣器设备节点
    蜂鸣器设备节点代码beep_gdevice.c:
#include "gdevice.h"struct gdevice beep_gdev = {.dev_name = "beep",.key = 0x45,.gpio_pin = 9,.gpio_mode = OUTPUT,.gpio_status = HIGH,.check_face_status = 0,.voice_set_status = 1,
};struct gdevice *add_beep_to_gdevice_list(struct gdevice *pgdevhead)
{//头插法return add_device_to_gdevice_list(pgdevhead, &beep_gdev);
};

beep_gdevice.h 代码如下:

#ifndef __BEEP_GDEVICE_H
#define __BEEP_GDEVICE_Hstruct gdevice *add_beep_to_gdevice_list(struct gdevice *pgdevhead);#endif

七、实现接收消息处理接口

同样参考voice接口实现receive 接口receive_interface.c代码

#include "gdevice.h"struct gdevice lock_gdev = {.dev_name = "lock",.key = 0x44,.gpio_pin = 8,.gpio_mode = OUTPUT,.gpio_status = HIGH,.check_face_status = 1,.voice_set_status = 1,
};
struct gdevice *add_lock_to_gdevice_list(struct gdevice *pgdevhead)
{//头插法return add_device_to_gdevice_list(pgdevhead, &lock_gdev);
};

receive.h 头文件代码

#ifndef ___RECEIVE_INTERFACE_H___
#define ___RECEIVE_INTERFACE_H___#include "control.h"struct control *add_receive_to_ctrl_list(struct control *phead);#endif

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/682883.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

奇异值分解(SVD)

对于一个方阵而言&#xff0c;采用的是特征分解&#xff0c;参考《矩阵特征值分解&#xff08;EVD&#xff09;-CSDN博客》

高程 | 数据的共享与保护(c++)

文章目录 &#x1f4da;标识符的作用域与可见性&#x1f407;作用域&#x1f407;可见性 &#x1f4da;对象的生存期&#x1f407;静态生存期&#x1f407;动态生存期 &#x1f4da;类的静态成员&#x1f407;静态数据成员&#x1f407;静态函数成员 &#x1f4da;类的友元&…

你的电脑关机吗

目录 程序员为什么不喜欢关电脑&#xff1f; 电脑长时间不关机会怎样? 电脑卡顿 中度风险 硬件损耗 能源浪费 散热问题 软件问题 网络安全问题 程序员为什么不喜欢关电脑&#xff1f; 大部分人都会选择将电脑进行关机操作。其实这不难理解&#xff0c;毕竟人类都需要…

MyBatis篇----第五篇

系列文章目录 文章目录 系列文章目录前言一、MyBatis 实现一对一有几种方式?具体怎么操作的?二、MyBatis 实现一对多有几种方式,怎么操作的?三、Mybatis 是否支持延迟加载?如果支持,它的实现原理是什么?四、Mybatis 的一级、二级缓存前言 前些天发现了一个巨牛的人工智能…

【数据库_MySQL】MySQL彻底卸载

程序员为什么不喜欢关电脑&#xff1f; 你是否注意到&#xff0c;程序员们似乎从不关电脑&#xff1f;别以为他们是电脑上瘾&#xff0c;实则是有他们自己的原因&#xff01;让我们一起揭秘背后的原因&#xff0c;看看程序员们真正的“英雄”本色&#xff01; 卸载 要是你的…

【机器学习案例3】从科学论文图片中提取标题、作者和摘要【含源码】

在这个项目中,我的目标是从科学论文图片中提取某些部分(标题、作者和摘要)。预期提取部分是科学论文中常见的部分,例如标题、摘要和作者。输入与最终结果。我的输入是将第一页纸转换成图像。最终结果是一个 txt 文件,其中包含标题、作者和摘要部分,如下图1和图2所示。我将…

SpringBoot整合第三方技术-缓存

&#x1f648;作者简介&#xff1a;练习时长两年半的Java up主 &#x1f649;个人主页&#xff1a;程序员老茶 &#x1f64a; ps:点赞&#x1f44d;是免费的&#xff0c;却可以让写博客的作者开心好久好久&#x1f60e; &#x1f4da;系列专栏&#xff1a;Java全栈&#xff0c;…

每日OJ题_递归①_力扣面试题 08.06. 汉诺塔问题

目录 递归算法原理 力扣面试题 08.06. 汉诺塔问题 解析代码 递归算法原理 递归算法个人经验&#xff1a;给定一个任务&#xff0c;相信递归函数一定能解决这个任务&#xff0c;根据任务所需的东西&#xff0c;给出函数参数&#xff0c;然后实现函数内容&#xff0c;最后找出…

linux内核原理--用户态线性地址空间,mmap,malloc,缺页异常

1.概述 前面我们介绍了内核态线性地址空间划分&#xff0c;及在内核态运行时&#xff0c;如何利用伙伴系统完成连续可用物理页框申请和释放。如何利用小块内存分配器实现高效的动态内存分配和释放。如何利用vmalloc&#xff0c;vfree完成线性地址连续但物理地址不连续的多个页框…

什么是 Flet?

什么是 Flet&#xff1f; Flet 是一个框架&#xff0c;允许使用您喜欢的语言构建交互式多用户 Web、桌面和移动应用程序&#xff0c;而无需前端开发经验。 您可以使用基于 Google 的 Flutter 的 Flet 控件为程序构建 UI。Flet 不只是“包装”Flutter 小部件&#xff0c;而是…

上位机图像处理和嵌入式模块部署(上位机主要功能)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 目前关于机器视觉方面&#xff0c;相关的软件很多。比如说商业化的halcon、vision pro、vision master&#xff0c;当然也可以用opencv、pytorch自…

使用 Chainlit, Langchain 及 Elasticsearch 轻松实现对 PDF 文件的查询

在我之前的文章 “Elasticsearch&#xff1a;与多个 PDF 聊天 | LangChain Python 应用教程&#xff08;免费 LLMs 和嵌入&#xff09;” 里&#xff0c;我详述如何使用 Streamlit&#xff0c;Langchain, Elasticsearch 及 OpenAI 来针对 PDF 进行聊天。在今天的文章中&#xf…

[缓存] - 2.分布式缓存重磅中间件 Redis

1. 高性能 尽量使用短key 不要存过大的数据 避免使用keys *&#xff1a;使用SCAN,来代替 在存到Redis之前压缩数据 设置 key 有效期 选择回收策略(maxmemory-policy) 减少不必要的连接 限制redis的内存大小&#xff08;防止swap&#xff0c;OOM&#xff09; slowLog …

Swift Combine 网络受限时从备用 URL 请求数据 从入门到精通十四

Combine 系列 Swift Combine 从入门到精通一Swift Combine 发布者订阅者操作者 从入门到精通二Swift Combine 管道 从入门到精通三Swift Combine 发布者publisher的生命周期 从入门到精通四Swift Combine 操作符operations和Subjects发布者的生命周期 从入门到精通五Swift Com…

数据结构.图的存储

一、邻接矩阵法 二、邻列表法 三、十字链表法

例39:使用List控件

建立一个EXE工程&#xff0c;在窗体上放一个文本框&#xff0c;一个列表框和三个按钮输入如下的代码&#xff1a; Sub Form1_Command1_BN_Clicked(hWndForm As hWnd, hWndControl As hWnd)List1.AddItem(Text1.Text)End SubSub Form1_Command2_BN_Clicked(hWndForm As hWnd, h…

【python之美】减少人工成本之批量拿取文件名保存_4

获取文件名保存 准备工作: 上代码: import ospath "C:\\Users\\Administrator\\Desktop\\text\\" file_names os.listdir(path) print(file_names)i 1 for file_name in file_names:name file_name.split(_)[0]print(name)new_name name "_修改后第&qu…

一周学会Django5 Python Web开发-项目配置settings.py文件-基本配置

锋哥原创的Python Web开发 Django5视频教程&#xff1a; 2024版 Django5 Python web开发 视频教程(无废话版) 玩命更新中~_哔哩哔哩_bilibili2024版 Django5 Python web开发 视频教程(无废话版) 玩命更新中~共计17条视频&#xff0c;包括&#xff1a;2024版 Django5 Python we…

Linux第54步_根文件系统第1步_编译busybox并安装_然后添加“根文件系统”的库

学习编译busybox&#xff0c;并安装&#xff0c;然后添加“根文件系统”的库。有人说busybox构建根文件系统&#xff0c;只适合学习&#xff0c;不适合做项目。 1、了解ubuntu的根文件系统 根文件系统的目录名为“/”&#xff0c;就是一个斜杠。 1)、输入“cd /回车”&…

Python爬虫之自动化测试Selenium#7

爬虫专栏&#xff1a;http://t.csdnimg.cn/WfCSx 前言 在前一章中&#xff0c;我们了解了 Ajax 的分析和抓取方式&#xff0c;这其实也是 JavaScript 动态渲染的页面的一种情形&#xff0c;通过直接分析 Ajax&#xff0c;我们仍然可以借助 requests 或 urllib 来实现数据爬取…