打卡机的设计——基本功能
打卡机设计
任务概述
应市场需求,某工程师现设计了一款新上下班打卡机,打卡机具有以下功能:
(1) 上班打卡,员工具有编号(首位为 1 的六位编号),输入编号后,再
输入校验码,校验码生成规则:员工编号除首位反序,再与员工编号
求和,如:员工编号,110086,校验码为 178087。校验码错误即打
卡失败。记录打卡时间
(2) 下班打卡,只需输入员工编号即可。记录打卡时间,显示该人员今天
上班时长,如果上班时长不够,显示早退 xx 分钟。可以更新下班打
卡时间。无下班打卡显示缺卡。
(3) 可以设置规定上班时长,如 9 小时
(4) 测试需要可以规定 6 秒=实际 1 小时,每次测试,输入指令后,开启
打卡机,打卡机开启模拟时间为:周一早上七点。程序运行结束为周
五晚 12 点。
(5) 实行弹性打卡制,如前一天上班时长超过规定时长 3 小时以上,第
二天迟到 2 小时以内不算迟到。
(6) 打卡机运行结束之前,每周该打卡机会生成每周考勤周报,显示周平
均上班时长,周迟到,早退,缺卡次数等。
设计过程
本次设计分两个步骤:
步骤一:完成打卡机的基本功能,比如:计时及打卡。
步骤二:完善打卡机的功能,比如:弹性打卡。
1.打卡机的基本功能
1.1-打卡机计时功能与功能列表的实现
因测试需要规定6s为1h,则打卡机的1s为10m、1ms为600ms;
有因为在计时的同时需要响应用户的操作(打卡…)所以需要开启输入函数的非阻塞模式
1.1.1-非阻塞的IO的使用
在记时的同时需要响应用户的打卡等功能,有因为scanf函数默认是阻塞的所以我们需要开启非阻塞io。
代码如下:
#include <stdio.h> #include <unistd.h> #include <fcntl.h> #include <sys/stat.h>int main(int argc, char const *argv[]) {int flags, flags2,test;//使用非阻塞ioif(flags = fcntl(STDIN_FILENO, F_GETFL, 0) < 0){perror("fcntl");return -1;}flags |= O_NONBLOCK;if(fcntl(STDIN_FILENO, F_SETFL, flags) < 0){perror("fcntl");return -1;}scanf("%d",&test);printf("#############1\n");usleep(2000000);printf("***************2\n");//关闭非阻塞ioflags &= ~O_NONBLOCK;if(fcntl(STDIN_FILENO, F_SETFL, flags) < 0){perror("fcntl");return -1;}scanf("%d",&test);return 0; }
1.1.2-打卡机记时功能实现与功能列表
要实现打卡机的时间记录功首先必须对打卡机的时候和现实时间进行对应最先想到的就是枚举
//实现代码 typedef unsigned long ulong; enum {milliseconds = 600; second = (milliseconds * 1000); minutes = (second * 60); hours = (minutes * 60); }
但是enum的成员变量都是int类型到hour的时候已经超出了int类型的表示范围。
所以换成结构体实现
//实现代码 typedef unsigned long ulong;struct MachineTime {ulong milliseconds; ulong seconds; ulong minutes; ulong hours; }TimeState;void machineTimer() {//打卡机时间对应 TimeState.milliseconds = 600; TimeState.seconds = (TimeState.milliseconds * 1000); TimeState.minutes = (TimeState.seconds * 60); TimeState.hours = (TimeState.minutes * 60); }
但使用这种方式又会导致每个时间都需要转换再计算,所以我们可以使用宏定义一个毫秒数在超出1000ms的时候对其进行转会成秒然后依次转化。
又因为需要存储5天(周一至周五)的数据所以可以将其存储在一个5行3列的数组当中。
在记时的同时我们可以用switch对输入的数据进行判断,实现功能列表功能。
用switch的好处在于可以美化代码,如使用if实现代码比较冗余;且用switch方便添加功能方便后期维护。
#include <stdio.h> #include <unistd.h> #include <fcntl.h> #include <sys/stat.h>#include "../head/utils.h"typedef unsigned char uint8_t; typedef unsigned long uint64_t;#define millionsecond 600int clockin_machine_start() {uint8_t timeRecorder[5][3] = {0};uint8_t currentDays = 0;char funcSelect;int flags;uint64_t msTmpRecord = 0;//周一早上七点打卡机开始工作timeRecorder[0][2] = 7;//开启非阻塞ioif(flags = fcntl(STDIN_FILENO, F_GETFL, 0) < 0){perror("fcntl");return -1;}flags |= O_NONBLOCK;if(fcntl(STDIN_FILENO, F_SETFL, flags) < 0){perror("fcntl");return -1;}printf("记时功能开始.\n");while (1){usleep(1000);//打卡机的1ms相当于600msmsTmpRecord += millionsecond;//ms -> sif (msTmpRecord >= 1000){timeRecorder[currentDays][0] += 1;msTmpRecord -= 1000;}//s -> mif (timeRecorder[currentDays][0] >= 60){timeRecorder[currentDays][1] += 1;timeRecorder[currentDays][0] -= 60;}//m -> hif (timeRecorder[currentDays][1] >= 60){timeRecorder[currentDays][2] += 1;timeRecorder[currentDays][1] -= 60;}//一天的时间超过了if (timeRecorder[currentDays][2] >= 24){currentDays += 1;}if (currentDays >= 5){break;}scanf("%c",&funcSelect);switch (funcSelect){case 'q':printf("签退\n");break;case 'r':printf("签到\n");break;default:break;}//保留测试接口用于退出记时if (funcSelect == 'S'){break;}}printf("记时功能结束.\n");printf("现在是星期%u %hhu时 %hhu分 %hhu秒.\n", (currentDays + 1), timeRecorder[currentDays][2], timeRecorder[currentDays][1], timeRecorder[currentDays][0]);// //关闭非阻塞io// flags &= ~O_NONBLOCK;// if(fcntl(STDIN_FILENO, F_SETFL, flags) < 0)// {// perror("fcntl");// return -1;// }}
1.2-打卡机打卡功能实现
打卡功能实现需要输出员工编号;然后对编号进行计算得到验证码。
#include <stdio.h> #include <unistd.h> #include <fcntl.h> #include <sys/stat.h> #include "../head/utils.h"#define millionsecond 600/* *********************************@函数功能:记时,功能列表 ********************************* */ int clockin_machine_start() {uint8_t timeRecorder[5][3] = {0};uint8_t checkInRecorder[5][3] = {0};uint8_t checkOutRecorder[5][3] = {0};uint8_t currentDays = 0;uint8_t flagCheckIn = 0;int flags;uint64_t msTmpRecord = 0;//开启非阻塞ioif(flags = fcntl(STDIN_FILENO, F_GETFL, 0) < 0){perror("fcntl");return -1;}flags |= O_NONBLOCK;if(fcntl(STDIN_FILENO, F_SETFL, flags) < 0){perror("fcntl");return -1;}//周一早上七点打卡机开始工作timeRecorder[0][2] = 7;printf("记时功能开始.\n");while (1){char funcSelect = 0;scanf("%c%*c",&funcSelect);switch (funcSelect){case 'q':checkOutRecorder[currentDays][0] = timeRecorder[currentDays][0]; checkOutRecorder[currentDays][1] = timeRecorder[currentDays][1];checkOutRecorder[currentDays][2] = timeRecorder[currentDays][2];printf("北京世界%u时 %u分 %u秒签退成功.\n",checkOutRecorder[currentDays][2], checkOutRecorder[currentDays][1], checkOutRecorder[currentDays][0]); break;case 'r':printf("签到\n");/**********************************@bug:关闭非阻塞IO的时候没有记时所以打卡函数执行的时候没有记时********************************* *///关闭非阻塞ioflags &= ~O_NONBLOCK;if(fcntl(STDIN_FILENO, F_SETFL, flags) < 0){perror("fcntl");return -1;} switch (sign_in()){case 0:flagCheckIn = 1;//打卡时间记录checkInRecorder[currentDays][0] = timeRecorder[currentDays][0]; checkInRecorder[currentDays][1] = timeRecorder[currentDays][1];checkInRecorder[currentDays][2] = timeRecorder[currentDays][2];printf("北京世界%u时 %u分 %u秒签到成功.\n",checkInRecorder[currentDays][2], checkInRecorder[currentDays][1], checkInRecorder[currentDays][0]);break;case -1:printf("签到失败,返回功能选择列表.\n");break;default:printf("打卡机异常请稍后再试.\n");break;}//开启非阻塞ioif(flags = fcntl(STDIN_FILENO, F_GETFL, 0) < 0){perror("fcntl");return -1;}flags |= O_NONBLOCK;if(fcntl(STDIN_FILENO, F_SETFL, flags) < 0){perror("fcntl");return -1;}break;default:break;}usleep(1000);//打卡机的1ms相当于600msmsTmpRecord += millionsecond;//ms -> sif (msTmpRecord >= 1000){timeRecorder[currentDays][0] += 1;msTmpRecord -= 1000;}//s -> mif (timeRecorder[currentDays][0] >= 60){timeRecorder[currentDays][1] += 1;timeRecorder[currentDays][0] -= 60;}//m -> hif (timeRecorder[currentDays][1] >= 60){timeRecorder[currentDays][2] += 1;timeRecorder[currentDays][1] -= 60;}//一天的时间超过了if (timeRecorder[currentDays][2] >= 24){currentDays += 1;flagCheckIn = 0;}if (currentDays >= 5){break;}//保留测试接口用于退出记时if (funcSelect == 'S'){break;}}printf("记时功能结束.\n");printf("现在是星期%u %hhu时 %hhu分 %hhu秒.\n", (currentDays + 1), timeRecorder[currentDays][2], timeRecorder[currentDays][1], timeRecorder[currentDays][0]);}/* *********************************@函数功能:签到打卡@参数:NULL @返回值:********************************* */ int sign_in() {int flag = 0;uint8_t input_count = 0;uint32_t employeeID = 0;uint32_t verifyCode = 0, verifyCodeIn = 0;printf("请输入您的工号:");scanf("%u%*c",&employeeID);putchar('\n');verifyCode = generator_verifyCode(employeeID);printf("请输入验证码:");scanf("%u%*c",&verifyCodeIn);putchar('\n');while (verifyCodeIn != verifyCode){input_count++;printf("验证码错误,请重新输入.您还剩%d次输入机会.",(3-input_count));printf("请输入验证码:");scanf("%u%*c",&verifyCodeIn);putchar('\n');if (input_count >= 3){flag = -1;break;}}return flag; } /* *********************************~@函数功能:生成验证码@参数:id -> 工号@返回值:********************************* */ uint32_t generator_verifyCode(uint32_t id){uint32_t oid = id % 100000;uint32_t tid = oid ;uint32_t checkcode = (id / 100000) * 100000; uint32_t nid = 0;//取出后面的数while ( tid != 0 ){nid = (nid * 10) + (tid % 10);tid = tid / 10;}//计算校验码checkcode = checkcode + (oid + nid);return checkcode; }
1.3-打卡机签退功能实现
打卡机的签退的时候需要判断是否签到,所以需要设置标志变量在打卡后和每天结束的时候进行打卡标志清除。
case 'q':if (flagCheckIn){flagCheckOut = 1;checkOutRecorder[currentDays][0] = timeRecorder[currentDays][0]; checkOutRecorder[currentDays][1] = timeRecorder[currentDays][1];checkOutRecorder[currentDays][2] = timeRecorder[currentDays][2];printf("北京世界%u时 %u分 %u秒签退成功.\n",checkOutRecorder[currentDays][2], checkOutRecorder[currentDays][1], checkOutRecorder[currentDays][0]); //工作时长}else{printf("您还没有签到,请您先进行签到.");}break;
2.打卡机功能完善
实现打卡机的早退、迟到缺卡和周总结函数
2.1-打卡机记录工作时长、早退、缺席
在一天结束的时候计算工作时长,再通过工作时长计算是否早退以及早退时间;
根据是否有打卡记录和签退记录判断是否缺席。
弹性打卡的标志也在此
//一天的时间超过了
if (timeRecorder[currentDays][2] >= 24)
{flexibility = 0;if (flagCheckIn && flagCheckOut){//工作时长计算uint8_t tmpMinute = 0;if (checkOutRecorder[currentDays][1] >= checkInRecorder[currentDays][1]){tmpMinute = checkOutRecorder[currentDays][1] - checkInRecorder[currentDays][1];}else{checkOutRecorder[currentDays][2]--;checkOutRecorder[currentDays][1] += 60;tmpMinute = checkOutRecorder[currentDays][1] - checkInRecorder[currentDays][1];}worktime[currentDays] = time_convert((checkOutRecorder[currentDays][2] - checkInRecorder[currentDays][2]), tmpMinute);printf("您今天工作了%u小时%u分钟.\n", (worktime[currentDays] / 60), (worktime[currentDays] % 60));if ((worktime[currentDays] >= FixedWorkingtime) && ((worktime[currentDays] - FixedWorkingtime) / 60) >=3){flexibility = 2;}else if (worktime[currentDays] < FixedWorkingtime){leaveEarly[currentDays] = (FixedWorkingtime - worktime[currentDays]);// printf("worktime = %u\n", worktime[currentDays]);// printf("leaveEarly = %u\n", leaveEarly[currentDays]);printf("您今天早退了%u小时 %u分钟.\n", leaveEarly[currentDays] / 60, \leaveEarly[currentDays] % 60);}}else{//缺席absence[currentDays] = 1;}currentDays += 1;flagCheckIn = 0;flagCheckOut = 0;employeeID = 0;}
2-2.打卡机迟到计算
根据上一段的弹性打卡标志再打卡成功的时候判断是否迟到
switch (sign_in(employeeID))
{case 0:flagCheckIn = 1;//打卡时间记录checkInRecorder[currentDays][0] = timeRecorder[currentDays][0]; checkInRecorder[currentDays][1] = timeRecorder[currentDays][1];checkInRecorder[currentDays][2] = timeRecorder[currentDays][2];printf("北京世界%u时 %u分 %u秒签到成功.\n",checkInRecorder[currentDays][2], checkInRecorder[currentDays][1], checkInRecorder[currentDays][0]);uint32_t flagLate = ((checkInRecorder[currentDays][2] - flexibility) > CheckInTime) ? 1 : 0; // printf("123=%u flagLate=%u\n", checkInRecorder[currentDays][2] - flexibility, flagLate); //迟到if (flagLate){arriveLate[currentDays] = 1; // printf("迟到!!!\n");}break;case -1:printf("签到失败,返回功能选择列表.\n");break;default:printf("打卡机异常请稍后再试.\n");break;
}
打卡机的设计——基本功能相关推荐
- DISCUZ开启设计插件功能和显示嵌入点功能
DISCUZ开启设计插件功能和显示嵌入点功能 找到config文件夹下的config_global.php文件,在最后添加 $_config['plugindeveloper'] = 0; $_con ...
- ios设计登录功能_亲爱的产品设计师,这是iOS 14的新功能
ios设计登录功能 On June 22, 2020 Apple previewed iOS 14 for the first time. As always there are quite some ...
- django20:BBS网页设计/注册功能/验证码代码
表设计 注册功能 """ 1.注册功能需要forms组件 不同功能,可单独一个py文件2.利用forms组件渲染前端标签1.利用ajax提交2.forms组件获取用户数据 ...
- 【安富莱二代示波器教程】第8章 示波器设计—测量功能
第8章 示波器设计-测量功能 二代示波器测量功能实现比较简单,使用2D函数绘制即可.不过也专门开辟一个章节,为大家做一个简单的说明,方便理解. 8.1 水平测量功能 8.2 ...
- Tangent Element调色台的设计与功能
Tangent Element调色台的设计与功能 今天小编为大家介绍一款从设计到功能都十分优秀的调色台--Tangent Element调色台.相信他的存在能让调色工作变得更加高效和轻松. 相信从事影 ...
- 使用Air724模块设计全功能DTU
使用Air724模块设计全功能DTU 目前物联网开发中,4G模块的使用方式有几下几种: MCU连接4G模块,使用串口通讯,MCU通过AT指令控制数据收发: 4G模块通过USB连接到ARM等系统,通过R ...
- java设计手机便签,解读一加手机系统便签App的设计和功能、易用性
原标题:解读一加手机系统便签App的设计和功能.易用性 大家都知道,对于系统自带应用而言,除了通话.短信.浏览器三大主要功能之外,其余用的最多的应属相机.音乐.文件管理器.便签四大家族了.一加氢OS也 ...
- 【奇思妙想】20个从设计到功能都创意十足的小众网站
现代生活节奏越来越快,工作压力也越来越大,不少设计师们在日复一日的重复工作中可能也会面临思维枯竭的困境. 摹客为大家整理了20个从设计到功能都充满趣味的网站,虽然功能类型各不相同,但这些网站收录了很多 ...
- 极简主义_网页设计中功能极简主义的真实性要少得多
极简主义 Written by Jessica Bennett 由杰西卡·贝内特 ( Jessica Bennett)撰写 介绍 (Introduction) "Less is more&q ...
最新文章
- 了解机器学习回归的3种最常见的损失函数
- android notification点击无效,Notification.addAction在Android O中无效
- [转] fedora linux下安装vmware tools的最好方法
- Git 之一 起源、安装、配置
- oracle取本月最后一天是星期几_Oracle 获取本周、本月、本季、现年的第一天和最后一天...
- java中properties作用,java中Properties有什么用,举例说明?
- 使用jMeter构造逻辑上有依赖关系的一系列并发请求
- 先来先服务算法、运行时间最短者优先算法和最高响应比优先调度算法_Linux进程调度:完全公平调度器CFS
- python智能推荐_Python与集体智能:第2章:推荐项目
- ACL'21 | 对比学习论文一句话总结
- Netty之Pipeline总结
- 2021-2027全球与中国弹簧加载探针市场现状及未来发展趋势
- TypeError: from_buffer() cannot return the address of the raw string within a str or unicode or byte
- Rethinking GPS: Engineering Next-Gen Location at Uber
- 证券投资深度学习_基于风险中性的深度学习选股策略
- 在win10系统安装linux系统,win10系统安装Linux子系统的操作方法
- 组图:码头上的搬运工
- 小程序 报错 errcode: 40029, errmsg: invalid code, hints: [ req_id: HQd79a0747th31 ]
- openEuler couldnt resolve host name
- 缓存数据库选型——redis和memcached