智能垃圾桶项目:Linux环境下

原理图

image.png

功能需求

  • 靠近时,垃圾桶开启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必须要被及时获取到。
然后项目中有许多的优化点,比如写入文件和写入数据库的时候,会发现写入的数据不对。

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值