这里写目录标题
功能需求
- 靠近时,垃圾桶开启2秒,2秒后关闭
- 垃圾桶开启带滴滴声
- 垃圾桶开启超过10秒,滴滴声警报
- 语音控制垃圾桶开关盖
- 回顾二阶段的Socket编程,实现Sockect客户端发送指令远程打开/关闭垃圾桶,并显示垃圾桶状态
- 统计当天垃圾桶开关盖次数及开关盖指令来源并记录在文件中
- 统计当天垃圾桶开关盖次数及开关盖指令来源并记录在数据库中
第一部分
1.靠近时,垃圾桶开启2秒,2秒后关闭
事实上,做内核开发的人知道,setitimer和sleep是冲突的!因为它们都使用了信号SIGPROF,所以会产生冲突。解决办法是:把setitimer(ITIMER_PROF, &val, NULL);换成setitimer(ITIMER_REAL, &val, NULL);。其中:ITIMER_PROF和SIGPROF对应,ITIMER_REAL和SIGALRM对应:因为sleep()和setitimer()会互相干扰,都会发送SIGLARM信号
实际上,对于Linux而言,两个不同进程
之间是不共享信号的,那么基于此,我们就可以通过使用进程来解决setitimer和sleep冲突的问题
2.垃圾桶开启带滴滴声 垃圾桶开启超过10秒,滴滴声警报
这里计时,采用开启一个线程来实现,其实第一部分,也可以通过开启线程来实现
开启bb线程,bb线程里面处理当垃圾桶开启时响1下,同时开始计时,超过了10s,再滴3s
计时demo
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/time.h>
#include <errno.h>
//定时3s,如果3s内没有解锁,那么输出
//如果3s内解锁了,那么不输出
pthread_mutex_t mutex;
pthread_cond_t cond;
//定时d秒,j秒后解锁
int d,j;
//定时器的初始化
//定时器多长时间后超时
void timeD_init(struct timespec* timeD)
{
timespec_get(timeD,TIME_UTC);
timeD->tv_sec += j;
printf("tv_sec=%ld\n",timeD->tv_sec);
}
int flag=0;
void* beep_wait_handler(void* arg)
{
//等待主线程释放mutex锁
pthread_mutex_lock(&mutex);
//开始sleep
printf("pthread_mutex_unlock=%d\n",pthread_mutex_unlock(&mutex));
sleep(d);
flag=1;
pthread_cond_signal(&cond);
pthread_exit(arg);
}
int main()
{
int arg=2;
//定时
struct timespec timeD;
//锁初始化
pthread_mutex_init(&mutex,NULL);
pthread_cond_init(&cond,NULL);
//开启线程
pthread_t beep_tidp;
char* ret=NULL;
while(1){
flag=0;
printf("定时d秒,j秒后超时\n");
//定时d秒,j秒后超时
scanf("%d %d",&d,&j);
printf("定时%d,%d后超时",d,j);
//锁住尽量确保定时的精准
pthread_mutex_lock(&mutex);
printf("lock run\n");
if(pthread_create(&beep_tidp,NULL,beep_wait_handler,(void*)&arg) != 0){
printf("beep_pthread create error\n");
perror("why");
exit(-1);
}
timeD_init(&timeD);
//主线程初始化完毕,释放锁,让子线程开始计时
pthread_mutex_unlock(&mutex);
//等待子线程计时完毕,或者超时
printf("flag1=%d\n",flag);
if(pthread_cond_timedwait(&cond,&mutex,&timeD)==ETIMEDOUT){
printf("超时了\n");
}
pthread_mutex_unlock(&mutex);
pthread_join(beep_tidp,(void**)&ret);
printf("flag2=%d\n",flag);
}
pthread_mutex_destroy(&mutex);
pthread_cond_destroy(&cond);
return 0;
}
对于这个计时demo有一个很奇怪的点,在子线程中释放了互斥锁,但是在主线程中却依然要去再解一次锁,否则主线程会一直阻塞等待mutex这个锁。有点不理解为什么,以后有时间再搞明白。
注意:
pthread_cond_timedwait(&cond,&mutex,&timeD)
有时会出现超时了依然阻塞的情况,是因为运行到这里时,mutex
锁无法被获取导致的,所以为了让其正常的超时时不阻塞,那么mutex
锁在运行:wq
时,必须是可获取状态
pthread的源码参考
int pthread_cond_timedwait(pthread_cond_t *restrict cond,
pthread_mutex_t *restrict mutex,
const struct timespec *restrict abstime)
{
int err = 0;
/* 获取互斥锁 */
if ((err = pthread_mutex_lock(mutex)) != 0) {
return err;
}
/* 根据cond的值挂起当前线程 */
err = __pthread_cond_wait(cond, mutex, abstime, 0);
/* 解除互斥锁 */
(void)pthread_mutex_unlock(mutex);
return err;
}
语音控制开关盖
这一块凸显出来的问题就是不同线程之间的调度问题,我太浅了,还没达到这个地步,将就着使用
主函数
#include <stdio.h>
#include <wiringPi.h>
#include <unistd.h>
#include <sys/time.h>
#include <stdlib.h>
#include <signal.h>
#include "hc.h"
#include <string.h>
#include "sg90.h"
#include <pthread.h>
#include "beep.h"
#include <wiringSerial.h>
#include "voice.h"
extern int jd;
extern int sg90_cnt;
double dis=0;//距离
char flag=0;//防止响多次
//舵机子进程
int pip = 0;
int hc_pipefd;
int pipefd[2];
char buf[24];
//语音
int voice_cmd=0;
int main()
{
int arg=100;
//初始化wiring为串口模式
if(wiringPiSetup() == -1){
fprintf(stderr,"%s","wiringPiSetup error\n");
exit(-1);
}
//初始化hc
hc_Init();
printf("hcInit\n");
//开启hc超声波线程
pthread_t hc_tidp;
if(pthread_create(&hc_tidp,NULL,hc_pthread_handler,(void*)&arg) != 0){
printf("hc_tidp pthread_create error\n");
perror("why:");
exit(-1);
}
//舵机相关
pip=pipe(pipefd);
if(pip==-1){
printf("error pip\n");
exit(-1);
}
int pid = fork();
if(pid == 0){
//关闭子进程的写通道
close(pipefd[1]);
//初始化sg90舵机
sg90_Init();
sg90_process_handler(pipefd[0]);
}else {
//关闭父进程的读通道
hc_pipefd = pipefd[1];
close(pipefd[0]);
}
//初始化蜂鸣器
beep_Init();
//开启bb线程
pthread_t beep_tidp;
if(pthread_create(&beep_tidp,NULL,beep_pthread_handler,(void*)&arg) != 0){
printf("beep_tidp pthread_create error\n");
perror("why");
exit(-1);
}
//语音控制开关盖
//voic_Init();
pthread_t voic_tidp;
if(pthread_create(&voic_tidp,NULL,voic_pthread_handler,(void*)&arg) != 0 ){
printf("voic_tidp pthread_create error\n");
perror("why");
exit(-1);
}
printf("voic_tidp\n");
//voic_pthread_handler();
while(1 && pid!=0){
sleep(2);
}
return 0;
}
超声波测距实现
#include <stdio.h>
#include <wiringPi.h>
#include <sys/time.h>
#include <unistd.h>
#include <string.h>
#define echo 9
#define trig 10
#define echo_value digitalRead(echo)
extern double dis;
extern char flag;
extern int hc_pipefd;//进程的pid
extern int voice_cmd;//语音的状态
void hc_Init()
{
//设置echo为输出模式
pinMode(echo,INPUT);
//设置trig为输入模式
pinMode(trig,OUTPUT);
}
double getDistance()
{
double dis;
struct timeval time_start;
struct timeval time_stop;
//怎么让它发波
//Trig,给Trig端口至少10us的高电平
digitalWrite(echo,LOW);
digitalWrite(trig,LOW);
usleep(5);
digitalWrite(trig,HIGH);
usleep(10);
digitalWrite(trig,LOW);
//怎么知道开始发了
//Echo信号,由低电平跳转到高电平,表示开始发送波
while(!echo_value);
gettimeofday(&time_start,NULL);
//怎么知道接收了返回波
//Echo,由高电平跳转回低电平,表示波回来了
while(echo_value){};
gettimeofday(&time_stop,NULL);
//怎么算时间
//Echo引脚维持高电平的时间!
long time = time_stop.tv_usec-time_start.tv_usec + (time_stop.tv_sec-time_start.tv_sec)*1000000;
dis = (340/2)*time*0.0001;
return dis;
}
void* hc_pthread_handler(void* arg)
{
while(1){
dis=getDistance();
printf("distance:%lf\n",dis);
if(dis<10 || voice_cmd == 1){
//只响一次
if(flag == 0){
flag=1;
write(hc_pipefd,"open",strlen("open"));
sleep(2);
voice_cmd = 0;
}
}else {
write(hc_pipefd,"close",strlen("close"));
flag=0;
}
sleep(1);
}
}
舵机控制实现
:::info
必须把他放到子进程中,因为多线程中信号是共享的,也就是sleep()和setitimer()函数是共享一个信号的,会互相打断运行,同一进程就更不用说
:::
#include <stdio.h>
#include <wiringPi.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define pwm 2
extern double dis;
extern char flag;
int jd=0;
int sg90_cnt=0;
void sg90_signal_handler(int signal)
{
if(signal == SIGALRM){
sg90_cnt++;
if(sg90_cnt <= jd){
digitalWrite(pwm,HIGH);
}else {
digitalWrite(pwm,LOW);
}
if(sg90_cnt == 40){
sg90_cnt=0;
digitalWrite(pwm,HIGH);
}
}
}
void timer_Init()
{
//定时器
struct itimerval timer;
//设定定时时长
timer.it_interval.tv_sec=0;
timer.it_interval.tv_usec=500;
//多长时间后开始
timer.it_value.tv_sec=1;
timer.it_value.tv_usec=0;
if(setitimer(ITIMER_REAL,&timer,NULL) == -1){
fprintf(stderr,"%s","setitimer() error\n");
exit(-1);
}
signal(SIGALRM,sg90_signal_handler);
}
void sg90_Init()
{
pinMode(pwm,OUTPUT);
timer_Init();
}
int sg90_process_handler(int pipefd)
{
char buf[24];
while(1){
//子进程只起到一个控制舵机的功能
//子进程不断从管道中获取值,一旦获取到相应的,那么就代表开关盖子
memset(buf,'\0',24);
read(pipefd,buf,24);
if(strstr(buf,"open")){
jd=5;
sg90_cnt=0;
}else if(strstr(buf,"close")){
jd=0;
sg90_cnt=0;
}
fflush(stdout);
}
return 0;
}
蜂鸣器实现
:::info
注意:pthread_cond_timedwait(&cond,&mutex,&timeD)
有时会出现超时了依然阻塞的情况,是因为运行到这里时,mutex
锁无法被获取导致的,所以为了让其正常的超时后不阻塞,那么mutex
锁在运行时,必须是可获取状态
:::
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/time.h>
#include <wiringPi.h>
#include <errno.h>
#define BEEP 6
#define beep_high digitalWrite(BEEP,HIGH)
#define beep_low digitalWrite(BEEP,LOW)
extern char flag;
void beep_Init()
{
pinMode(BEEP,OUTPUT);
digitalWrite(BEEP,HIGH);
}
pthread_mutex_t mutex;
pthread_cond_t cond;
//定时d秒,j秒后解锁
//int d,j;
//定时器的初始化
//指定定时几秒
struct timespec timeD_init()
{
struct timespec timeD;
timespec_get(&timeD,TIME_UTC);
timeD.tv_sec += 10;
return timeD;
}
void* beep_wait_handler(void* arg)
{
//等待主线程释放mutex锁
pthread_mutex_lock(&mutex);
pthread_mutex_unlock(&mutex);
//开始sleep
//等待flag变为0
while(flag != 0);
pthread_cond_signal(&cond);
pthread_exit(arg);
}
//beep main pthread
//beep函数的主线程
void* beep_pthread_handler(void* arg)
{
struct timespec timeD;
//锁初始化
pthread_mutex_init(&mutex,NULL);
pthread_cond_init(&cond,NULL);
//开启线程
pthread_t beep_tidp;
char* ret=NULL;
while(1){
beep_high;
while(flag==0);
beep_low;
sleep(1);
beep_high;
pthread_mutex_unlock(&mutex);
pthread_mutex_lock(&mutex);
printf("lock run\n");
if(pthread_create(&beep_tidp,NULL,beep_wait_handler,arg) != 0){
printf("beep_pthread create error\n");
perror("why");
exit(-1);
}
timeD=timeD_init();
//主线程初始化完毕,释放锁,让子线程开始计时
pthread_mutex_unlock(&mutex);
//等待子线程计时完毕,或者超时
while(pthread_cond_timedwait(&cond,&mutex,&timeD) == ETIMEDOUT){
printf("flag=%d\n",flag);
beep_low;
sleep(2);
break;
}
pthread_mutex_unlock(&mutex);
pthread_join(beep_tidp,(void**)&ret);
}
pthread_mutex_destroy(&mutex);
pthread_cond_destroy(&cond);
return 0;
}
语音控制
语音控制偶尔失效的原因我猜测是线程调度问题,如果放到主线程中,运行应该不会出现偶尔失效的问题。
voice.c
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>
#include "uartTools.h"
int voic_fd;
extern int voice_cmd;
void voic_Init()
{
if((voic_fd = myserialOpen("/dev/ttyS5",9600)) < 0){
printf("mySerialOpen() error\n");
perror("why");
exit(-1);
}
}
void* voic_pthread_handler(void* arg)
{
voic_Init();
char cmd;
printf("voic_fd=%d\n",voic_fd);
while(1){
cmd = serial_Get_char_one(voic_fd);
printf("this is cmd=%c\n",cmd);
switch(cmd){
case 'O':
printf("open\n");
voice_cmd=1;
break;
case 'C':
printf("close\n");
voice_cmd=0;
break;
}
/*
这一个while()的作用是用于提高线程优先级,如果疑惑,可以尝试将这段while删除
看看语音控制是否会偶尔失效
*/
while(serialDataAvail(voic_fd));
sleep(1);
}
pthread_exit(arg);
}
uartTools.c
#include <string.h>
#include <termios.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "wiringSerial.h"
int myserialOpen (const char *device, const int baud)
{
struct termios options ;
speed_t myBaud ;
int status, fd ;
switch(baud)
{
case 9600: myBaud = B9600 ; break ;
case 115200: myBaud = B115200 ; break ;
default:
return -2 ;
}
if ((fd = open (device, O_RDWR | O_NOCTTY | O_NDELAY | O_NONBLOCK)) == -1)
return -1 ;
fcntl (fd, F_SETFL, O_RDWR) ;
// Get and modify current options:
tcgetattr (fd, &options) ;
cfmakeraw (&options) ;
cfsetispeed (&options, myBaud) ;
cfsetospeed (&options, myBaud) ;
options.c_cflag |= (CLOCAL | CREAD) ;
options.c_cflag &= ~PARENB ;
options.c_cflag &= ~CSTOPB ;
options.c_cflag &= ~CSIZE ;
options.c_cflag |= CS8 ;
options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG) ;
options.c_oflag &= ~OPOST ;
options.c_cc [VMIN] = 0 ;
options.c_cc [VTIME] = 100 ; // Ten seconds (100 deciseconds)
tcsetattr (fd, TCSANOW, &options) ;
ioctl (fd, TIOCMGET, &status);
status |= TIOCM_DTR ;
status |= TIOCM_RTS ;
ioctl (fd, TIOCMSET, &status);
usleep (10000) ; // 10mS
return fd ;
}
int serial_Get_char_one(const int fd)
{
char ch;
read(fd,&ch,1);
return ch;
}
第二部分
- 回顾二阶段的Socket编程,实现Sockect客户端发送指令远程打开/关闭垃圾桶,并显示垃圾桶状态
- 统计当天垃圾桶开关盖次数及开关盖指令来源并记录在文件中
- 统计当天垃圾桶开关盖次数及开关盖指令来源并记录在数据库中
Socket
demo如下
服务器端
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <pthread.h>
void error_handler(char* reason)
{
printf("%s error\n",reason);
perror("why");
exit(-1);
}
int socket_connect=0;
void* read_pthread_handler(void* arg)
{
char read_buf[1024];
while(1){
memset(read_buf,'\0',strlen(read_buf));
if(read(socket_connect,read_buf,1024) == -1){
error_handler("read()");
}
printf("client:%s\n",read_buf);
fflush(stdout);
}
pthread_exit(arg);
}
int main(int argc,char** argv)
{
if(argc<2){
error_handler("argc");
exit(-1);
}
int socket_fd=0;
//1.创建套接字
//int socket(int domain, int type, int protocol)
if((socket_fd = socket(AF_INET,SOCK_STREAM,0)) == -1){
error_handler("socket()");
}
printf("socket success\n");
//2.为套接字添加信息:IP地址和端口
//int bind(int sockfd,const struct sockaddr *addr,socklen_t addrlen)
struct sockaddr_in addr;
memset(&addr,0,sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(atoi(argv[1]));
if(inet_aton("192.168.31.75",&(addr.sin_addr)) == 0){
error_handler("inet_aton()");
}
if(bind(socket_fd,(struct sockaddr*)&addr,sizeof(addr)) == -1){
error_handler("bind()");
}
printf("bind success\n");
//3.监听网络连接
//int listen(int sockfd,int backlog);
if(listen(socket_fd,2) == -1){
error_handler("listen()");
}
printf("listen success\n");
//4.监听到有客户端接入,接收一个连接
//int accept(int sockfd,struct sockaddr* addr,socklen_t* addrlen)
int addr_len = sizeof(addr);
if((socket_connect = accept(socket_fd,(struct sockaddr*)&addr,&addr_len))==-1){
error_handler("accept()");
}
//5.数据交互
//read()、write()
char buf[1024] = "hello,this is service\n";
//开启一个子线程负责读取客户端的发送
//主线程负责发送
pthread_t socket_pthread_tidp;
int arg=10;
if(pthread_create(&socket_pthread_tidp,NULL,read_pthread_handler,(void*)&arg) != 0){
error_handler("socket_pthread");
}
while(1){
memset(buf,'\0',strlen(buf));
scanf("%s",buf);
write(socket_connect,buf,strlen(buf));
fflush(stdin);
}
//收取线程资源
pthread_join(socket_pthread_tidp,NULL);
//6.关闭套接字,断开连接
//close()
close(socket_fd);
return 0;
}
客户端
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <pthread.h>
#include <arpa/inet.h>
int socket_id =0;
void error_handler(char* reason){
printf("%s error\n",reason);
perror("why?");
exit(-1);
}
void* chail_read_pthread_handler(void* arg)
{
char read_buf[1024];
while(1){
memset(read_buf,'\0',strlen(read_buf));
read(socket_id,read_buf,1024);
printf("service:%s\n",read_buf);
fflush(stdout);
}
pthread_exit(arg);
}
//子线程负责读,主线程负责写
int main(int argc,char** argv)
{
if(argc<2){
error_handler("argc<2");
}
//int socket(int domain, int type, int protocol)
if((socket_id = socket(AF_INET,SOCK_STREAM,0)) == -1){
error_handler("socket()");
}
printf("socket success\n");
//int connect(int sock, struct sockaddr *serv_addr, socklen_t addrlen);
struct sockaddr_in addr;
memset(&addr,0,sizeof(addr));
addr.sin_family=AF_INET;
addr.sin_port=htons(atoi(argv[1]));
inet_aton("192.168.31.75",&(addr.sin_addr));
if(connect(socket_id,(struct sockaddr*)&addr,sizeof(addr)) == -1){
error_handler("connect()");
}
//write()、read()
//create child process
pthread_t read_tidp;
int arg=11;
if(pthread_create(&read_tidp,NULL,chail_read_pthread_handler,(void*)&arg));
char write_buf[1024];
while(1){
memset(write_buf,'\0',1024);
scanf("%s",write_buf);
write(socket_id,write_buf,strlen(write_buf));
fflush(stdin);
}
pthread_join(read_tidp,NULL);
//close()
close(socket_id);
return 0;
}
这一套demo有问题,当服务器端或者客户端断开时,另一端会疯狂刷屏
然后,项目进行到这里,必须抛弃管道了(可能是我不太熟练的原因),替代品是共享内存,同时为了避免竞争,socket控制开关盖使用信号控制
共享内存demo
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <stdlib.h>
//创建两个子进程,一个子进程向父进程中写,一个子进程读父进程中写的东西
int pid1,pid2;
void one_process(int share_id)
{
void* one_addr = shmat(share_id,0,0);
char buf[1024];
while(1){
memset(buf,'\0',strlen(buf));
scanf("%s",buf);
strcpy((char*)one_addr,buf);
sleep(1);
}
shmdt(one_addr);
}
void two_process(int share_id)
{ void* two_addr = shmat(share_id,0,0);
perror("why:");
while(1){
printf("this is two read:%s\n",(char*)two_addr);
sleep(1);
}
shmdt(two_addr);
}
int main()
{
//创建共享内存
key_t key = ftok(".",12);
int share_id = shmget(key,1024*4,IPC_CREAT|0600);
void *map_address = shmat(share_id,0,0);
printf("shmat:OK\n");
pid1 = fork();
if(pid1 == 0){
//这里负责读
one_process(share_id);
return 0;
}
pid2 = fork();
if(pid2 == 0){
//负责写
two_process(share_id);
return 0;
}
while(1){
sleep(1);
}
shmctl(share_id,IPC_RMID,0);
return 0;
}
统计当天垃圾桶开关盖次数及开关盖指令来源并记录在文件中
统计当天:获取系统的日期,即年月日然后进行判断
开关盖有三个来源,分别是socket、超声波以及语音控制
当dis<10时,超声波开盖加1
当dis>10时,超声波关盖加1
socket发送一次open,socket开盖+1
发送一次close,socket关盖+1
语音控制一个open,语音开盖+1
发送一次close,语音关盖+1
测试的demo
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
void error_handler(char* reason);
void file_Init(int fd);
void config_ration(int fd);
time_t now;
//today date
char now_date[50];
//file data
char buf[500];
char addr_buf[200];
time_t now;
struct tm* tm_now;
char datetime[200];
int gd_len=0;
//数字转换
char num_buf[10];
//得到数字
int getNum(char* addr)
{
char* p = addr;
memset(num_buf,'\0',strlen(num_buf));
int i=0;
while((*p) != '\n'){
num_buf[i++] = *p;
p++;
}
printf("num1=%s\n",num_buf);
return atoi(num_buf);
}
//将数字塞进去
void setNum(char* addr,int int_num)
{
char* p = addr;
memset(num_buf,'\0',strlen(num_buf));
printf("sprintf\n");
sprintf(num_buf,"%d%c",int_num,'\0');
printf("stop sprintf\n");
int i=0;
while(num_buf[i] != '\0'){
*p=num_buf[i++];
p++;
}
}
//第二个参数是指,来源是什么,如果是hc_open,那么sorce=hc_open
void date_Init(int fd,char* sorce)
{
//注意光标位置,运行到这里时,光标要么在首部(<450),要么在基于尾部的450处(>450)
//每一次开关盖,都获取当天日期,然后比对,如果没有那么运行初始化
//获取
time(&now);
tm_now = localtime(&now);
strftime(datetime,200,"%Y-%m-%d\n",tm_now);
char* str_addr=NULL;
//比对
if((str_addr = strstr(buf,datetime)) == NULL){
//如果没有,那么就代表是新的一天,需要重新建立数据
//运行完毕后buf中会重新填满数据
config_ration(fd);
str_addr = strstr(buf,datetime);
}
//运行到了这里,代表文件中是一定有对应日期的相应数据
//str_addr一定是指向了第一次出现对应日期的首地址
//移动光标到相应首地址位置
int len = str_addr-buf;
lseek(fd,len,SEEK_CUR);
//那么重新获取相应的地址填充addr_buf
//计算确认要读取多少个字节
int str_num = strlen(buf)-len;
//准备读取数据
read(fd,addr_buf,str_num);
//再次移动光标到addr_buf对应的首地址处
lseek(fd,-str_num,SEEK_END);
//获取对应数据的首地址
char* num_addr = strstr(addr_buf,sorce);
//移动num_addr到数据处
num_addr = num_addr+strlen("hc_open =");
//将数字读取出来
printf("addr_buf:%s\n",addr_buf);
int num = getNum(num_addr);
num = num+1;
setNum(num_addr,num);
write(fd,addr_buf,strlen(addr_buf));
}
int main()
{
int fd = open("record",O_RDWR|O_CREAT,0600);
gd_len = strlen("hc_open =0 \n");
if(fd == -1){
error_handler("fd error\n");
}
file_Init(fd);
date_Init(fd,"hc_open");
close(fd);
return 0;
}
//读取文件,将光标移动到文件尾部,判断是否超过了450
//如果超过了,那么只读取450个字节,如果没有超过,那么全部读取出来
//这样做是为了尽量避免buf溢出
//这一个程序只负责移动光标以及填充buf
void file_Init(int fd)
{
//移动光标到头部,保证进入这一段程序时,光标一定在头部
lseek(fd,0,SEEK_SET);
int file_num = lseek(fd,0,SEEK_END);
lseek(fd,0,SEEK_SET);
if(file_num>450){
//大于450,那么移动光标
lseek(fd,-450,SEEK_END);
}
//冲刷buf
memset(buf,'\0',strlen(buf));
//读取文件
read(fd,buf,450);
//读取完毕,将光标移回去
if(file_num>450){
lseek(fd,-450,SEEK_END);
}else {
lseek(fd,0,SEEK_SET);
}
}
void error_handler(char* reason)
{
printf("%s error\n",reason);
perror("why?");
exit(-1);
}
void config_ration(int fd){
//移动光标到尾部
lseek(fd,0,SEEK_END);
//写入数据
//这里依然可以优化,也就是如果是不对齐的呢?
//时间
write(fd,datetime,strlen(datetime));
//hc num
write(fd,"hc_open =0 \n",gd_len);
write(fd,"hc_close =0 \n",gd_len);
//voice num
write(fd,"voice_open =0 \n",gd_len);
write(fd,"voice_close =0 \n",gd_len);
//socket num
write(fd,"socket_open =0 \n",gd_len);
write(fd,"socket_close=0 \n",gd_len);
//写入完毕,再次重新刷新buf,(这是一个优化点)
//printf("strlen=%ld\n",strlen("socket_open =0\n"));
file_Init(fd);
}
统计当天垃圾桶开关盖次数及开关盖指令来源并记录在数据库中
1张表
create table datetime(
id INTEGER PRIMARY KEY AUTOINCREMENT,
dateTime TEXT NOT NULL,
hc_open INT DEFAULT 0,
hc_close INT DEFAULT 0,
voice_open INT DEFAULT 0,
voice_close INT DEFAULT 0,
socket_open INT DEFAULT 0,
socket_close INT DEFAULT 0
);
insert into datetime values(NULL,"2023-5-8",0,0,0,0,0,0);
sqlite测试demo
#include <stdio.h>
#include <stdlib.h>
#include <sqlite3.h>
#include <string.h>
#include <time.h>
char exist_sql();
void table_Init();
void get_time();
int date_exist();
void add_ljt();
char buf[1024];
char sql_buf[100];
sqlite3* sql_fd;
int exist_callback(void *arg, int column_size, char *column_value[], char *column_name[])
{
int* count=(int*)arg;
*count = atoi(column_value[0]);
return 0;
}
int select_callback(void *arg, int column_size, char *column_value[], char *column_name[])
{
int i=0;
for(;i<column_size;++i){
printf("%d,%s:%s\n",i,column_name[i],column_value[i]);
}
int count = atoi(column_value[2]) + 1;
memset(sql_buf,'\0',strlen(sql_buf));
sprintf(sql_buf,"update ljt set hc_open=%d where id=%s;",count,column_value[0]);
printf("update=%s\n",sql_buf);
sqlite3_exec(sql_fd,sql_buf,NULL,NULL,NULL);
return 0;
}
int main()
{
int date_flag=-1;
char exist_sql_flag = exist_sql();
sqlite3_open("sql.db",&sql_fd);
if(exist_sql_flag == 0){
table_Init();
}
date_flag = date_exist();
//辨别当天日期是否存入
if(date_flag == -1){
printf("date_exist() error\n");
exit(-1);
}else if(date_flag == 0){
//如果没有数据,那么添加一条
add_ljt();
}
printf("datetime=%s,strlen()=%ld\n",buf,strlen(buf));
//更新对应数据
sqlite3_exec(sql_fd,"select * from ljt;",select_callback,NULL,NULL);
return 0;
}
void add_ljt()
{
memset(sql_buf,'\0',strlen(sql_buf));
sprintf(sql_buf,"insert into ljt values(NULL,\"%s\",0,0,0,0,0,0);",buf);
sqlite3_exec(sql_fd,sql_buf,NULL,NULL,NULL);
}
int date_exist()
{
int count=-1;
get_time();
memset(sql_buf,'\0',strlen(sql_buf));
snprintf(sql_buf,sizeof(sql_buf),"select count(*) from ljt where %s;",buf);
printf("%s\n",sql_buf);
sqlite3_exec(sql_fd,sql_buf,exist_callback,&count,NULL);
return count;
}
void get_time()
{
//获取时间
memset(buf,'\0',strlen(buf));
time_t now;
struct tm* tm_now;
time(&now);
tm_now = localtime(&now);
strftime(buf,200,"%Y-%m-%d",tm_now);
}
void table_Init(){
char* table="create table ljt("\
"id INTEGER PRIMARY KEY AUTOINCREMENT,"\
"dateTime char(50) NOT NULL,"\
"hc_open INT DEFAULT 0,"\
"hc_close INT DEFAULT 0,"\
"voice_open INT DEFAULT 0,"\
"voice_close INT DEFAULT 0,"\
"socket_open INT DEFAULT 0,"
"socket_close INT DEFAULT 0"\
")";
sqlite3_exec(sql_fd,table,NULL,NULL,NULL);
}
//检测是否存在sql.db文件
char exist_sql()
{
memset(buf,'\0',strlen(buf));
FILE* file = popen("ls","r");
fread(buf,1024,1,file);
if(strstr(buf,"sql.db") == NULL){
return 0;
}
return 1;
}
总体代码
链接: gitHub
总结
这一个项目让我发现了非常多的问题,比如上面提到的使用条件变量时,要注意mutex必须要被及时获取到。
然后项目中有许多的优化点,比如写入文件和写入数据库的时候,会发现写入的数据不对。