使用RC522、mysql、树莓派构成的图书馆管理系统

起因是RFID课要求做一个课程设计,结果感觉本来该搞搞硬件给搞成软件了。总这这篇博客是偏软件程序的。

一、总体环境

1.硬件

树莓派4B、RFID-RC522

2.软件

Python3、mysql

二、硬件连线

参考文章:基于RFID RC522模块制作树莓派通读卡器
上面这片文章很详细。懂单片机和树莓派的看得懂,不懂的照着接线也行(哈哈)

接线后效果图:

三、Python环境

需要自己添加的依赖库有pymysqlprettytable
在命令行输入下列命令下载:

$~pip3 install pymysql
$~pip3 install prettytable 

pymysql库是python操作mysql数据库的API集合
prettytable库是用来画表格好看的

四、程序设计思路

本程序是模拟图书馆管理员的视角,一共有四个操作:1.借书2.还书3.移除书4.寻找书

以下是程序的逻辑:(丑不叽叽)图丑见谅
最后一步都是对数据库的操作

五、具体步骤

  1. 建立数据库
    因为是模拟学校图书馆的管理系统,所以学生信息表(Student)、书籍信息表(Book)应该是提前就完善了的。还有一个表是借还表(BR),用来记录学生的借书情况

    先用简单的扫描程序(参见后面的read.py)扫描手上有的rfid标签的id(懒得改id了所以就用标签初始的),然后用id建立Studnet和Book表
    我用的手上两个圆的标签当学生卡,六个卡片标签当书籍,标签上的标记是方便调试和演示写的
    在这里插入图片描述

    所以建立的数据库如下:

    从上往下依次是Student,Book,BR
    (这几个库都是提前建立的,id都分别对应我手上的rfid标签,一些特殊数值的设定是为了方便之后演示)
    在这里插入图片描述

  2. 程序
    程序没什么难的,一些库的操作网上搜一搜总会找到,有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. 演示运行

    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)

都先记录在这里,之后有机会再完善。

本文是记录给自己看兼分享,如果有建议或者是代码的问题欢迎留言讨论。

  • 4
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值