关于c语言的打卡机思路与代码


一、任务要求

应市场需求,某工程师现设计了一款新上下班打卡机,打卡机具有以下功能:
(1)上班打卡,员工具有编号(首位为 1 的六位编号),输入编号后,再输入校验码,校验码生成规则:员工编号除首位反序,再与员工编号求和,如:员工编号,110086,校验码为 178087。校验码错误即打卡失败。记录打卡时间 。
(2)下班打卡,只需输入员工编号即可。记录打卡时间,显示该人员今天上班时长,如果上班时长不够,显示早退 xx 分钟。可以更新下班打卡时间。无下班打卡显示缺卡。
(3)可以设置规定上班时长,如 9 小时
(4)测试需要可以规定 6 秒=实际 1 小时,每次测试,输入指令后,开启打卡机,打卡机开启模拟时间为:周一早上七点。程序运行结束为周五晚 12 点。
(5)实行弹性打卡制,如前一天上班时长超过规定时长 3 小时以上,第二天迟到 2 小时以内不算迟到。
(6)打卡机运行结束之前,每周该打卡机会生成每周考勤周报,显示周平均上班时长,周迟到,早退,缺卡次数等。

二、思路

1.整个程序主要分为以下几个部分

1.1主函数
1.2非阻塞性函数
1.3待机函数
1.4判断打卡机是否开始工作函数
1.5打卡机工作函数
1.6显示时间函数
1.7上午打卡函数
1.8下午打卡函数
2.1头文件

1.1主函数

由于这是模拟打卡机,测试的时候会出现时间跨度大的情况如上午下午缺卡,也会出现时间跨度小的情况如早退迟到。所以要灵活选择测试时间比例。

主函数代码如下:

/*
* @file		打卡机
* @version	1.0 
* @author	cyw
* @date 	2020/9/28
* 1.测试以6s为1h, 打卡机设定从0点开始工作,show函数显示的是系统时间,也就是09时代表09时,主要调试早退迟到等情况
* 2.测试以60s为1h,打卡机设定从0点开始工作,show函数显示的是系统时间,也就是09分代表09时,主要调试缺卡这种时间跨度大的状况
*/

#include<stdio.h>
#include<time.h>
#include"punch_card.h"

int main(int argc,char *argv[])
{ 

	unsigned int N = 0,code = 0;
	unsigned char j = 0,n = 0,week = 0;
	unsigned char morning_card_shortage = 0,night_card_shortage = 0,timelate = 0,leave_early = 0;
	float total_time = 0,average_time = 0;

	time_t timep;
   	time (&timep);
   	p = gmtime(&timep); 	
   	week = p->tm_wday;
   	member.week= week;

    N = member.code;
   	
   	while(week < 4)//方便调试,星期数自加                                
    {     
    	clockin_machine_start();//判断打卡机是否开始工作                                                
    	work_start();                          	
    	week++;
    	member.week= week;                                       
    }                                                
                                                    
    N = member.code;
 
   	for(j = 1;j < 6;j++)//汇总一星期各项数据
   	{
   		timelate              += 			member.number[N].work_data[j].timelate;
   		leave_early           += 			member.number[N].work_data[j].leave_early;
   		morning_card_shortage += 			member.number[N].work_data[j].morning_card_shortage;
   		night_card_shortage   += 			member.number[N].work_data[j].night_card_shortage;
   		total_time 			  += 			member.number[N].work_data[j].total_time; 
   	}
   	
   	week = week - 3;
   	average_time = (total_time / week);
   	
   	printf("打卡周报:\r\n");
   	printf("周迟到数 %d次\r\n",timelate);
   	printf("周早退次数:%d次\r\n",leave_early);
   	printf("周上午缺卡次数:%d次\r\n",morning_card_shortage);
   	printf("周下午缺卡次数:%d次\r\n",night_card_shortage);
   	printf("周上班平均时长:%.2f小时\r\n",average_time);
   	
   	return 0;
}

1.2非阻塞性输入函数

c语言的scanf函数是阻塞性输入函数,即如果没有输入,程序则会停在当前位置。c++中有非阻塞性输入函数但是c语言无法引用,所以提供给读者一个c语言版本的非阻塞性输入函数,读者可以直接引用,注意其宏定义。

非阻塞性输入函数宏定义如下:

#define TTY_PATH “/dev/tty”
#define STTY_US "stty raw -echo -F "
#define STTY_DEF "stty -raw echo -F "

非阻塞性输入函数代码如下:

int _my_getchar(void)//非阻塞输入函数
{
    fd_set rfds;
    struct timeval tv;
    int ch = 0;
	
    system(STTY_US TTY_PATH);

    FD_ZERO(&rfds);
    FD_SET(0, &rfds);
    tv.tv_sec = 0;
    tv.tv_usec = 6000000; //设置等待超时时间..6s
 
    if (select(1, &rfds, NULL, NULL, &tv) > 0)//检测键盘是否有输入
    {
        ch = getchar(); 
    }

    return ch;
}

1.3待机函数

通过非阻塞性输入函数我们可以制作一个待机函数,当非阻塞性输入函数检测到员工编号输入时,会逐个显示输入的数字,当输入6个数字则会进入上午打卡函数。未满6个数字则会被上午打卡函数重复调用直至满6个数字。如果非阻塞性输入函数没有检测到有数字输入会待机6秒(可设置),6秒后会调用show函数输出当前时间,然后也会被上午打卡函数重复调用,这样就完成待机界面。

待机函数代码如下:

int employee_info_get(int *number)
{
	char temp = 0;
	static char num_string[6] = {0};
	static unsigned char i = 0;
	char count = 0;
	
	temp = _my_getchar();
	if(0 != temp)
	{
		if('q' == temp)//q表示结束程序
		{
			system(STTY_DEF TTY_PATH);
			return 2;
		}
		
		printf("%c\r\n",temp);
		
		if(i < 5)
		{
			num_string[i]  = temp;
			i++;
			return 0;
		}
		else
		{
			num_string[5]  = temp;
			i = 0;
			//把字符数组的内容转换成整型
			//"123456" ->  123456
			*number = atoi(num_string);			
			//当一个数组重复使用时候,在用完的时候把数组清空
			memset(num_string,0,sizeof(num_string));
			return 1;
		}
				
	}
	else
	{
		printf("当前时间为:");//如果12s内没有输入就进入待机状态,显示当前时间
    	show();
    	count = member.count;
    	count++;
    	member.count = count;
	}
	
	return 0;
}

1.4判断打卡机是否开始工作函数

通过时间显示函数获取当前时间,如果是双休日打卡机则不工作并发出提示后退出,注意0代表星期天。

判断打卡机是否开始工作函数代码如下:

int clockin_machine_start(void)//判断打卡机是否开始工作函数
{
   	unsigned char hour = 0,week = 0;
   	
   	time_t timep;
   	time (&timep);
   	p = gmtime(&timep);
   	
   	week = p->tm_wday;
   	member.week= week;
   	hour = 8 + p->tm_hour;
   	
   	if(week == 0 || week == 6)//如果是星期六或星期日打卡机不工作
   	{
   		printf("今天是星期%d请于工作时间打卡\r\n",week);exit(0);
   	}
    
	return 0;
}

1.5打卡机工作函数

打卡机工作函数代码如下:

int work_start(void)//打卡机工作函数
{
	unsigned int N = 0,code = 0;
	unsigned char count = 0,n = 0,week = 0;
	unsigned char morning_card_shortage = 0,night_card_shortage = 0,flag_morning =0,flag_night = 0;
		
	clock_in_morning(); //上午打卡函数
	
	N = member.code;
	week = member.week;
	count = 0;
	member.count = count;
	
  	clock_in_night();  //下午打卡函数
  	
  	getchar();   
  	
  	flag_morning = member.number[N].work_data[week].flag_morning;
  	flag_night = member.number[N].work_data[week].flag_night;

  	if(flag_morning == 0)//flag_morning==0代表上午没有打卡,缺卡次数+1
  	{  		 
         member.number[N].work_data[week].morning_card_shortage++;
  	}
  	else
  	{
  		
  	}
  	
  	if(flag_night == 0)//flag_night==0代表下午没有打卡,缺卡次数+1
  	{
        member.number[N].work_data[week].night_card_shortage++;
  	}
  	else
  	{
  	
  	}
  	
  	count = 0;
  	return 0;
}

1.6显示时间函数

通过地址访问获取系统时间

显示时间函数代码如下:

void show(void)//显示时间函数
{
	unsigned char week = 0;
	
   	time_t timep;
   	time (&timep);
   	p = gmtime(&timep);
   	
   	week = member.week;
   	//显示年月日时分秒以及星期
   	printf("%d年 ",1900 + p->tm_year);
   	printf("%d月",1 + p->tm_mon);
   	printf("%d日",p->tm_mday);
   	printf("%d时",8 + p->tm_hour);
   	printf("%d分",p->tm_min);
   	printf("%d秒",p->tm_sec);
   	printf("---------- 星期%d\r\n",week);
}

1.7上午打卡函数

通过result判断进入哪一阶段,result = 1则进入上午打卡函数后续阶段,result = 2则测试结束退出程序,result为其他比如为0则重新循环,在重复循环过程中就会重复调用待机函数。而后续阶段主要包含验证员工编号、判断是否迟到、判断是否上午缺卡、判断是否满足弹性打卡制度。

上午打卡函数代码如下:

int clock_in_morning(void)//上午打卡函数
{
 	unsigned int tmp = 0,N = 0,code = 0,number = 0,number1 = 0,check_number = 0,check_code_right = 0,first_time = 0,last_time = 0;
 	unsigned char min = 0,hour = 0,week = 0,result = 0,count = 0,flag_morning = 0,card_time = 0;
 	unsigned char morning_card_shortage = 0,timelate = 0,late_hour = 0;
 	float total_time = 0;

	time_t timep;
	time (&timep);
	p = gmtime(&timep);
   
  	while(1)//当检测到有完整6位的员工编号输入时进入后续阶段,否则进入待机状态(显示当前时间)
  	{
	    count = member.count;
   		result = employee_info_get(&number);
   		min = p->tm_min; 
   		if(12 == count)//如果12小时内没有输入,判定上午缺卡并结束
   		{
   			card_time = 12;
   			member.card_time = card_time;
			return 0;
   		}
   		if(2 == result)
   		{
			printf("sys over\r\n");
			return 0;
   		}
   		else if(1 == result)//后续阶段
  			 {  			    			   
   				N = number;
   				member.code = number;
   				week = member.week;
   				member.number[N].work_data[week].flag_morning++;//上午打卡标志 		
   				
   				number1 = number - 100000;
   				
   				while(number1 != 0)//反序
 				{
      				tmp = tmp * 10 + number1 % 10;
      				number1 = number1 / 10;
   				}
   							
   				check_code_right = number + tmp;
   				
   				printf("请输入验证码:");
   				scanf("%d",&check_number);
   				printf("%d\r\n",check_number);
   				
   				L1:if(check_number == check_code_right)
      			   {
        				printf("当前时间为:");
       			   	    show();//显示当前时间函数
        				first_time = p->tm_sec;//将分钟换算成秒钟,方便测试
       					min = p->tm_min; 
      					first_time = min * 60 + first_time; 
      					member.number[N].work_data[week].first_time = first_time; 
     			   }
     			 else
   				  {
       					printf("验证码错误,请重新输入:\r\n");
       					scanf("%d",&check_number);
       					printf("%d\r\n",check_number);
       					goto L1;
     			  }
     			  
       			  hour=8 + p->tm_hour;   
       			  card_time = p->tm_hour;//min
       			  member.card_time = card_time;     			 
       			       
       			  if(hour > 8 && hour <= 9)//8-9点打卡视为打卡成功if(hour > 8 && hour <= 9)
                       {
                             printf("上班打卡成功\r\n");  
                             break;
                       }       			
          		  else if(hour > 9 && hour <= 10)//9-10点打卡视为迟到if(hour > 9 && hour <= 10)
                       {
             		   		 printf("打卡成功不过您迟到了!\r\n");
             		 		 late_hour = hour - 9;//计算迟到时间(如果迟到的话),没迟到是负值也没事,这个值只保存一天会被后一天替代
             		 		 member.number[N].work_data[week].late_hour = late_hour;             		 		
            				 member.number[N].work_data[week].timelate++;
              				 if(member.number[N].work_data[week].late_hour <= 2 && (member.number[N].work_data[week-1].total_time-9) >= 3)//前一天加班时长超过3小时及以上,第二天迟到2小时以内不算迟到!
						     {
								printf("不过,鉴于您昨天加班超过规定上班时间3小时以上以及今日迟到时间在2小时以内,今日不算迟到!\r\n");
								member.number[N].work_data[week].timelate--;//避免重复计算						
						     } 
              				 break;
            	 	   }            	
                  else if(hour > 10)//当天10点后打卡都视为上午缺卡 
                  {  
            		    printf("上午缺卡\r\n");             		      
        				member.number[N].work_data[week].morning_card_shortage++;         		    
            			break;          		
                  } 
	           }
	}
	
  	getchar();
	return 	0;
}

1.8下午打卡函数

下午打卡函数思路与上午打卡函数类似

下午打卡函数代码如下:

int clock_in_night(void)//下午打卡函数
{

	unsigned int tmp = 0,N = 0,code = 0,T = 0,number = 0,number1 = 0,check_number = 0,check_code_right = 0,first_time = 0,last_time = 0;
 	unsigned char min = 0,hour = 0,week = 0,result = 0,count = 0,flag_night = 0,card_time = 0;
 	unsigned char night_card_shortage = 0,timelate = 0,late_hour = 0,leave_early = 0;
 	float total_time = 0;
 	
	time_t timep;
	time (&timep);
	p = gmtime(&timep);
	  	
   	while(1)//当检测到有完整6位的员工编号输入时进入后续阶段,否则进入待机状态(显示当前时间)
  	{
   		
   		L2:result = employee_info_get(&number);
   		card_time = member.card_time;
   		count = member.count;
   		if((22 - card_time) == count)//如果通过上午打卡函数,会得到card_time,card_time为上午打卡时间
   		{                            //如果没有通过上午打卡函数,说明上午0-12点都没打卡,card_time为固定12.在22点前没有进行下午打卡,判定下午缺卡
			return 0;
   		}
   		if(2 == result)
   		{
			printf("sys over\r\n");
			return 0;
   		}
   		else if(1 == result)//后续阶段
			{
   				N = number;
   				member.code = number;
   				week = member.week;
   				member.number[N].work_data[week].flag_night++;//下午打卡标志
   				number1 = number - 100000;                                 
   				
   				while(number1 != 0)//反序
 				{
      				tmp = tmp * 10 + number1 % 10;
      				number1 = number1 / 10;
   				}
   							
   				check_code_right = number + tmp;
   				
   				printf("请输入验证码:");
   				scanf("%d",&check_number);
   				printf("%d\r\n",check_number);
   				
   
   				L3:if(check_number == check_code_right)
      			   {
        				printf("当前时间为:");
       			   	    show();
        				last_time = p->tm_sec;
       					min = p->tm_min; 
      					last_time = min * 60 + last_time; 
      					member.number[N].work_data[week].last_time = last_time; 
     			   }
     			 else
   				  {
       					printf("验证码错误,请重新输入:\r\n");
       					scanf("%d",&check_number);
       					printf("%d\r\n",check_number);
       					goto L3;
     			  } 
    
      					total_time = (member.number[N].work_data[week].last_time - member.number[N].work_data[week].first_time) / 6;   //60   				    
					    member.number[N].work_data[week].total_time = total_time; 
					  
					  	hour = 8 + p->tm_hour;
					  	min = p->tm_min; 
					  		
					  	if(total_time < 9)//公司规定时长为9小时
		        		{
		        		      printf("离公司规定时长还差%.2f小时\r\n",9 - total_time); 	
		        		}	
		        		else
		        		{
		        		      printf("已经完成规定时长,超额%.2f小时\r\n",total_time - 9);
		        		}        		 
					  						 
						if(hour < 18)//18点前打卡下班视为早退if(hour < 18)
		      		    {
		        		      printf("您早退下班了!");
		        		      printf("今天上班时长:%.2f小时\r\n",total_time);
		        		      		        		          
							  member.number[N].work_data[week].leave_early++;
							  
		        		      printf("是否取消下班打卡,取消请输入1并回车确定,确认请输入0并回车确定\r\n");//可以取消下午打卡,重新计算下班时间和工作时长
							  scanf("%d",&T);
										
							   if(T == 1) //取消下午打卡,重新计算下班时间和工作时长
								{
							 		 member.number[N].work_data[week].total_time = 0;
							  		 member.number[N].work_data[week].leave_early--;//避免重复计算
							  		 tmp=0;
							  		 getchar();
							  		 goto L2;
								}
								else if(T == 0)//选择下班
									 {
									 	  printf("好的,不选择重新打卡\r\n");
									 	  printf("今天上班时长:%.2f小时\r\n",total_time);
									 	  return 0;
									 }
		      		    }
		      		    else//18点后下班成功
		      		    {
		      		    	  printf("今天上班时间:%.2f小时\r\n",total_time);
		      		    	  printf("下班打卡成功\r\n");
		      		    	  return 0;	      		    	  
		      		    }
       			  		   			    									  						           
			}
       
	}
    
 	return 0;      
}

2.1头文件

头文件代码如下:

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<time.h>
#include<memory.h>
#include<unistd.h>

#define TTY_PATH            "/dev/tty"
#define STTY_US             "stty raw -echo -F "
#define STTY_DEF            "stty -raw echo -F "

#ifndef punch_card_H
#define punch_card_H

struct MEMBER{                             
                int code;									//存储员工编号
                char week;									//存储当前星期
                char count;
                char card_time;
              	 struct NUMBER
              	 {
              	 	struct WORK_DATA	
           		   {             		               		
               	 	int first_time;							//当天早上打卡时间,以秒为单位
             		int last_time;							//当天下午打卡时间,以秒为单位
				   	char late_hour;							//当天迟到时间(如果迟到的话)
				   	char morning_card_shortage;				//当天早上缺卡
				   	char night_card_shortage;				//当天下午缺卡
				   	char timelate;							//当天迟到
				   	char leave_early;						//当天早退
				   	char flag_morning;						//上午打卡标志
				   	char flag_night;						//下午打卡标志
				   	double total_time;						//当天工作总时间				   	
            	   }work_data[7]; 							//存储该员工整个星期工作信息
              	 }number[199999];				 			//存储公司全部员工信息
            }member;
struct tm *p;                                               //存储时间信息

void show(void);											//显示时间函数
int _my_getchar(void);										//非阻塞输入函数
int employee_info_get(int *number);							//非阻塞输入函数
int clock_in_morning(void);								    //上午打卡函数
int clock_in_night(void);						 			//下午打卡函数
int clockin_machine_start(void);							//判断打卡机是否开始工作函数
int work_start(void);										//打卡机开始工作函数

#endif

三、总结

这个打卡机程序是之前写的,还有很多不足和可以改进的地方,将来会找时间加以改进,希望这篇文章能够对大家有所帮助。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值