起因是RFID课要求做一个课程设计,结果感觉本来该搞搞硬件给搞成软件了。总这这篇博客是偏软件程序的。
一、总体环境
1.硬件
树莓派4B、RFID-RC522
2.软件
Python3、mysql
二、硬件连线
参考文章:基于RFID RC522模块制作树莓派通读卡器
上面这片文章很详细。懂单片机和树莓派的看得懂,不懂的照着接线也行(哈哈)
接线后效果图:
三、Python环境
需要自己添加的依赖库有pymysql
、prettytable
在命令行输入下列命令下载:
$~pip3 install pymysql
$~pip3 install prettytable
pymysql
库是python操作mysql数据库的API集合
prettytable
库是用来画表格好看的
四、程序设计思路
本程序是模拟图书馆管理员的视角,一共有四个操作:1.借书2.还书3.移除书4.寻找书
以下是程序的逻辑:(丑不叽叽)
最后一步都是对数据库的操作
五、具体步骤
-
建立数据库
因为是模拟学校图书馆的管理系统,所以学生信息表(Student)、书籍信息表(Book)应该是提前就完善了的。还有一个表是借还表(BR),用来记录学生的借书情况先用简单的扫描程序(参见后面的read.py)扫描手上有的rfid标签的id(懒得改id了所以就用标签初始的),然后用id建立Studnet和Book表
我用的手上两个圆的标签当学生卡,六个卡片标签当书籍,标签上的标记是方便调试和演示写的
所以建立的数据库如下:
从上往下依次是Student,Book,BR
(这几个库都是提前建立的,id都分别对应我手上的rfid标签,一些特殊数值的设定是为了方便之后演示)
-
程序
程序没什么难的,一些库的操作网上搜一搜总会找到,有python和数据库基础就可以写出这个程序来。主要繁琐的地方是一些类型的转换和操作逻辑之类的。硬件程序参考我上面提到的文章,懒得自己写的话照着弄就可以,我主要是参考read.py的程序,加上了mysql数据库的操作。
以下为我的主程序:
# libraryManage.py
import time
import RPi.GPIO as GPIO
import MFRC522
import pymysql as db
import re
import datetime
import prettytable as pt
def get_id():
Reader = MFRC522.MFRC522()
while True:
# Scan for cards
(status,TagType) = Reader.MFRC522_Request(Reader.PICC_REQIDL) # Get the UID of the card
(status,uid) = Reader.MFRC522_Anticoll()
# If we have the UID, continue
if status == Reader.MI_OK:
# Print UID
uid = str(uid[0]) + str(uid[1]) + str(uid[2]) + str(uid[3]) # 获取学生id
print(uid)
GPIO.cleanup()
return uid
def days(str1,str2):
date1=datetime.datetime.strptime(str1[0:10],"%Y-%m-%d")
date2=datetime.datetime.strptime(str2[0:10],"%Y-%m-%d")
num=(date1-date2).days
return num
def Student_Scan(oper):
# 读取学生id
print("扫描学生中...\n")
S_id = get_id()
if S_id is not None:
print("完成扫描.\n")
if oper == 1: # 操作为借书
# 判断该学生是否有书未还
conn = db.connect('localhost', user = 'root', passwd ='数据库密码', db = 'libraryManagement') # 连接数据库
cur = conn.cursor() # 获得指针
res = cur.execute('select borrow_time from BR where S_id = \'{}\';'.format(S_id)); # (2020, 10, 20)
while 1:
res = cur.fetchone()
if res is not None:
res = res[0].strftime('%Y-%m-%d %H:%M:%S') # 转换为str
mt = re.match(r'([0-9]{4})-([0-9]+)-([0-9]+)', res)
today = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') # 今天的日期
borrow_time = datetime.datetime.strptime('{}-{}-{}'.format(mt.group(1), mt.group(2), mt.group(3)),'%Y-%m-%d')
borrow_time = borrow_time.strftime('%Y-%m-%d %H:%M:%S')
borrow_day = days(today, borrow_time) # 借书天数
if borrow_day > 90:
print("有书已超时,请还书后再借!\n") # 超时
return -1
else:
continue
else:
break
cur.close() # 关闭指针
conn.close() # 关闭连接
return S_id # 学生借书单中不存在超时未还的书
elif oper == 2: # 操作为还书
return S_id
else:
print("扫描失败!\n")
return 0
def Book_Scan():
# 读取书id
print("扫描书籍中...\n")
B_id = get_id()
if B_id is not None:
print("完成扫描.\n")
return B_id
else:
print("扫描失败!\n")
return 0
def Borrow_Book(oper):
S = Student_Scan(oper)
if S == -1:
return -2 # 超时未还退出
time.sleep(2)
B = Book_Scan()
if S != 0:
if B != 0:
conn = db.connect('localhost', user = 'root', passwd = 'wuliwen44', db = 'libraryManagement') # 连接数据库
# 将借书信息插入BR表
cur = conn.cursor() # 获得指针
cur.execute('insert into BR values(\'{}\', \'{}\', \'{}\');'.format(B, S, datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')))
cur.close() # 关闭指针
conn.commit() # 确认操作
# 修改学生借书信息Student
cur = conn.cursor() # 获得指针
cur.execute('update Student set S_borrowNum=S_borrowNum+1 where S_id = \'{}\';'.format(S))
cur.close() # 关闭指针
conn.commit() # 确认操作
# 修改书籍借还信息Book
cur = conn.cursor() # 获得指针
cur.execute('update Book set borrow_or_not=true where B_id = \'{}\';'.format(B))
cur.close() # 关闭指针
conn.commit() # 确认操作
conn.close() # 关闭连接
return 1
else:
return -1 # 书籍扫描失败退出
else:
return -1 # 学生扫描失败退出
def Return_Book(oper):
S = Student_Scan(oper)
time.sleep(2)
B = Book_Scan()
if S != 0:
if B != 0:
# 从BR中删除记录
conn = db.connect('localhost', user = 'root', passwd = 'wuliwen44', db = 'libraryManagement') # 连接数据库
cur = conn.cursor() # 获得指针
cur.execute('delete from BR where B_id = \'{}\' and S_id = \'{}\';'.format(B, S))
cur.close() # 关闭指针
conn.commit() # 确认操作
# 修改学生借书信息Student
cur = conn.cursor() # 获得指针
cur.execute('update Student set S_borrowNum=S_borrowNum-1 where S_id = \'{}\';'.format(S))
cur.close() # 关闭指针
conn.commit() # 确认操作
# 修改Book
cur = conn.cursor() # 获得指针
cur.execute('update Book set borrow_or_not=0 where B_id = \'{}\';'.format(B))
cur.close() # 关闭指针
conn.close() # 关闭连接
return 1
else:
return -1 # 书籍扫描失败退出
else:
return -1 # 学生扫描失败退出
def Delete_Book():
B = Book_Scan()
if B != 0:
# 从Book删除记录
conn = db.connect('localhost', user = 'root', passwd = '数据库密码', db = 'libraryManagement') # 连接数据库
cur = conn.cursor() # 获得指针
cur.execute('delete from Book where B_id = \'{}\';'.format(B))
cur.close() # 关闭指针
conn.commit() # 确认操作
conn.close() # 关闭连接
return 1
else:
return -1 # 书籍扫描失败退出
def Find_Book():
B = input("请输入书籍名称:\n")
if B is not None:
# 从Book中查找
conn = db.connect('localhost', user = 'root', passwd = 'wuliwen44', db = 'libraryManagement') # 连接数据库
# 打印所有书籍信息
tb_1 = pt.PrettyTable()
cur = conn.cursor() # 获得指针
n = cur.execute('select * from Book where B_name= \'{}\';'.format(B))
tb_1.field_names = ['B_id', 'B_name', 'B_author', 'B_type', 'borrow_or_not']
while 1:
res = cur.fetchone()
if res is None:
break
tb_1.add_row([res[0], res[1], res[2], res[3], '{}'.format(res[4])])
print(tb_1)
cur.close() # 关闭指针
# 打印书籍数量和可借数量
# 书籍总数
book_num = []
cur = conn.cursor() # 获得指针
cur.execute('select B_name, count(*) from Book where B_name = \'{}\';'.format(B)) # 书籍总数
res = cur.fetchone()
if res is None:
book_num.append(res[0]) # 书名
book_num.append(res[1]) # 总数
cur.close() # 关闭指针
# 可借数量
cur = conn.cursor()
cur.execute('select count(*) from Book where B_name = \'{}\' and borrow_or_not = false;'.format(B)) # 可借数量
res = cur.fetchone()
if res is not None:
book_num.append(res[0]) # 可借数
else:
book_num.append(0) # 没有可借的书
cur.close()
tb_2 = pt.PrettyTable()
tb_2.field_names = ['B_name', 'Total_Number', 'Left_Number']
tb_2.add_row(book_num)
print(tb_2)
conn.close() # 关闭连接
return 1
else:
return -1 # 书籍扫描失败退出
# 选择菜单
menu = """
Please select an operate(1-5):
1.Borrow Book
2.Return Book
3.Delete Book
4.Find Book
5.EXIT
"""
while 1:
oper = input(menu)
oper = int(oper)
if oper == 1:
BB = Borrow_Book(oper)
if BB == 1:
print("借书操作完成.\n")
elif BB == -1:
print("错误退出.\n")
elif BB == -2:
print("借书操作中断.\n")
elif oper == 2:
RR = Return_Book(oper)
if RR == 1:
print("还书操作完成.\n")
else:
print("错误退出.\n")
elif oper == 3:
DD = Delete_Book()
print(DD)
if DD == 1:
print("移除书籍操作完成.\n")
else:
print("错误退出.\n")
elif oper == 4:
FF = Find_Book()
if FF == 1:
print("寻找书籍操作完成.\n")
else:
print("错误退出.\n")
elif oper == 5:
exit()
把这个文件丢到上面文章里下载的文件里就可以了
程序注释还算写的比较清楚,以下只解释几个地方:
1.首先学生借书的时间:
我是直接用datetime获得当前时间,然后strftime
转化为格式化的字符串,例如 2020-11-23 01:50 然后就可以插入mysql的查询语句中了
today = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
2.判断学生超时与否
用刚才一样的语句获得当前时间,查询BR数据库中该学生的借书记录,得到借书的时间,然后将两个日期提取出来,计算两个日期差的天数,大于90天(3个月)就提示超出时间。中间用到了正则表达式。
mysql查询语句如下:
select borrow_time from BR where S_id = 'id';
3.查找书籍
查找书籍输入书籍名字得到的结果有
1该名称的所有书籍的所有信息
2图书馆该书籍的总量(包括未借走的和借走的)
3图书馆中该书籍的剩余量
12就不用说了,3是找的Book库中的borrow_or_not属性,ture表示该书已被借走,不在图书馆中
mysql查询语句:
select B_name, count(*) from BR wehre B_name = '书名'; --找书总量
select count(*) from BR where B_name = '书名' and borrow_or_not = false; --找剩余数量
查询数据库后将得到的元组元素放到一个列表book_num里,之后交给python处理,为了输出的格式好看用到了prettytable库。
还有一件事,扫描标签的函数中,加了延时,这个很重要,因为如果不加,读取速度就会很快,根本来不及替换标签。
-
演示运行
1.Talia借书WordPowerMadeEasy
操作完成之后Student数据库中Talia的借书数量+1,BR库中多了一条记录,借书的时间为当前时间,Book库中,WPME这本书的“借出与否”属性设置为true(忘了截图了)
2.Ivy借书
Ivy预先的数据库中已经借了一本书,并且距离借书时间超过了三个月,所以不能借书,要还书后再借
3.Ivy还书WhatIf?
还书操作后BR中删掉借书的那条记录,Student中Ivy借书数量-1,Book中该书的“借出与否”设为false
4.移除书《1894》
操作后Book库中删除该书信息记录
5.找书TimeMachine
返回所有同名书籍的信息、该书剩余数量和可借数量。找不到的书输出全是0。
六、总结
其实因为做的时间比较短,能力也有限,有很多功能没有加上,本来之前是想用两个rc522分线程来读取的(没仔细推敲逻辑),嫌麻烦最后还是只用了一个,加了延时感觉还可以。
我没加 添加书籍 的操作选项,因为要加这个的话,标签中的信息需要改,时间不太够我再去写这个,就没加。
还有查找书籍想再加一个按作者查找和按类别查找
扫描到学生或者书籍时输出名字(现在输出的是id)
都先记录在这里,之后有机会再完善。
本文是记录给自己看兼分享,如果有建议或者是代码的问题欢迎留言讨论。