文章目录

  • 一、任务要求
  • 二、思路
    • 1.整个程序主要分为以下几个部分
    • 1.1主函数
    • 1.2非阻塞性输入函数
    • 1.3待机函数
    • 1.4判断打卡机是否开始工作函数
    • 1.5打卡机工作函数
    • 1.6显示时间函数
    • 1.7上午打卡函数
    • 1.8下午打卡函数
    • 2.1头文件
  • 三、总结

一、任务要求

应市场需求,某工程师现设计了一款新上下班打卡机,打卡机具有以下功能:
(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; //设置等待超时时间..6sif (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;//minmember.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_Hstruct 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

三、总结

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

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

  1. python编程剪刀石头布思路_剪刀石头布小习题三种语言python2、php、go代码

    剪刀石头布小习题三种语言python2.php.go代码 # coding:utf-8 """ python核心编程6-14习题的解题思路 设计一个"石头,剪子 ...

  2. python登录代码思路_用python登录Dr.com思路以及代码分享

    用python登录Dr.com思路以及代码分享 发布于 2014-08-28 22:31:52 | 192 次阅读 | 评论: 0 | 来源: 网友投递 Python编程语言Python 是一种面向对 ...

  3. 最全中文leetcode解题攻略:思路知识点代码...搞定AI大厂笔试

    本文经AI新媒体量子位(公众号ID:qbitai)授权转载,转载请联系出处. 本文约多图,建议阅读5分钟. 本文为你分享中文leetcode解题攻略,助你通过AI大厂笔试. 当代程序员的困惑可能大致分 ...

  4. 50行的python游戏代码_50行代码实现贪吃蛇(具体思路及代码)

    [下载文档:  50行代码实现贪吃蛇(具体思路及代码).txt ] (友情提示:右键点上行txt文档名->目标另存为) 50行代码实现贪吃蛇(具体思路及代码) 最近一直在准备用来面试的几个小de ...

  5. C语言面试高频问题:自己代码实现字符串相关的常用API

    参考:C语言面试高频问题:自己代码实现字符串相关的常用API 作者:图触靓 发布时间: 2020-08-08 13:31:33 网址:https://blog.csdn.net/bhbhhyg/art ...

  6. C语言一趟冒泡交换最小值,C语言单链表冒泡排序为啥以下代码实现不了?

    struct node *sort(struct node *head)/*排序*/ { struct node *p,*q; struct node *temp; for(p=head;p!=NUL ...

  7. 最全中文leetcode解题攻略:思路知识点代码都有,搞定AI大厂笔试

    铜灵 发自 凹非寺 量子位 出品 | 公众号 QbitAI 当代程序员的困惑可能大致分为两类:一是"35岁之后我该去往何处",二是"如何才能进入互联网大厂工作" ...

  8. Glcm 灰度共生矩阵,保姆级别教程,获取图片的Glcm和基于Glcm的纹理特征,附讲解思路,python代码的实现

    保姆级别教程,获取图片的Glcm和基于Glcm的纹理特征,附讲解思路,python代码的实现 网络上Glcm的原理很多,但是实现的python代码我确实没找到,讲的也不是很清楚 此文介绍了如何在一张图 ...

  9. c语言选择冒泡排序,c语言选择冒泡排序讲解(附代码)

    c语言选择冒泡排序讲解(附代码) c语言选择冒泡排序讲解(附代码) 冒泡排序原理举例: 给定一组数 15 20 1 16 进行从大到小冒泡排序.第一次起泡的第一次比较:用15和20比较,若15比20小 ...

最新文章

  1. ~~~端午安康~~~
  2. Python生物信息学⑤DNA转录RNA
  3. git回退到之前版本和合并分支查看当前分支切换分支
  4. Brocade NOS学习笔记(第一章——第三章)
  5. 20-forEach循环语句
  6. 解密 云HBase 冷热分离技术原理
  7. 安全公司正在借 AI 之力,预测、防御并抵抗网络攻击
  8. bzoj 4660 Crazy Rabbit——LIS解决“相交”限制的思想
  9. php判断检测一个数组里有没有重复的值
  10. CANopen笔记2
  11. Python Day56-57 js
  12. java对MP4视频编码转换为H264格式解决浏览器播放无画面问题
  13. 谈谈网络协议,常见的网络协议有那些?
  14. msvcr71.dll丢失的解决方法,如何快速进行丢失修复?
  15. visio用例图箭头怎么画_visio2010绘制用例图带图例.docx
  16. 移动端适配之一:到底什么是像素
  17. linux 批量下载fnl,TMS370C356FNL
  18. SMD元件尺寸大小公制英制对应说明
  19. css实现单边斜切效果
  20. L2-027. 名人堂与代金券

热门文章

  1. 最流行的android组件大全
  2. 最新!盘点杭州2023年最值得加入的互联网公司!
  3. 【SpringCloud】
  4. Adobe Dreamweave下载和安装步骤
  5. 我的世界1.72 Java下载_我的世界1.7.2java下载 java路径有误怎么设置
  6. Linux- Top命令查看系统资源状况
  7. threejs实现天空盒
  8. C标准-->C89/C99/C11
  9. 插件--旋钮插件jQuery knob
  10. 《Nodejs开发加密货币》之三:Nodejs让您的前端开发像子弹飞一样