前の記事
この記事の続きになります。
実現したいこと
入退室管理のようなことをしたいと思っています。
前回FelicaからIDmを読み取ることができたので、今回はFelicaを事前に登録し、タッチを繰り返すことでonlineとofflineを切り替えるようなものを作ります。
##SQLite3を使ってみる
全く知識がなかったので以下の記事を参考にしました。SQLの文法は随時検索して学びました。
【python】データベースの入門としてSQLiteを使ってみた
テーブルを生成する
テーブル名はFelica、カラムは name、 IDm、 time、 statusにしました。型が適当になってしまったのは改善したいと思っています。textで良かったんでしょうか。
import sqlite3
con = sqlite3.connect('./idm_list.db')
c = con.cursor()
# 生成する
sql = """
create table Felica (name varchar(64), IDm varchar(200), time varchar(32), status varchar(32))
"""
c.execute(sql)
con.commit()
レコードを追加する
import sqlite3
con = sqlite3.connect('./idm_list.db')
c = con.cursor()
sql = 'insert into Felica (name, IDm, time, status) values (?,?,?,?)'
user1 = ('tanaka', '01010a10d9243d05', '20:37', 'offline')
user2 = ('suzuki', '012f2d178b4d6f8a', '10:21', 'offline')
c.execute(sql,user1)
c.execute(sql,user2)
con.commit()
テーブルの中身を確認する
import sqlite3
import datetime
# 確認
con = sqlite3.connect('./idm_list.db')
c = con.cursor()
select_sql = 'select * from Felica'
for row in c.execute(select_sql):
print(row)
con.close()
実装
- 事前にテーブルを作って、カードのレコードを登録しておく
- カードをタッチするとstatusとtimeを更新する
- statusはonline,offline
- Ctrl-Cで終了するまで待機し続ける
import binascii
import nfc
import signal
import datetime
import sqlite3
class MyCardReader(object):
def on_connect(self, tag):
print ("読み取り中")
self.idm = binascii.hexlify(tag.idm)
return True
def read_idm(self):
clf = nfc.ContactlessFrontend('usb')
try:
clf.connect(rdwr = {'on-connect': self.on_connect})
finally:
clf.close()
class DatabaseEdit:
def updatetime(self,idm):
dt_now = datetime.datetime.now()
now = dt_now.strftime('%H:%M')
con = sqlite3.connect('./idm_list.db')
c = con.cursor()
# 前回の時間とステータスを取得
select_sql = "select * from Felica where IDm='%s'" % idm
for row in c.execute(select_sql):
lasttime = row[2]
status = row[3]
name = row[0]
print(name + "さん")
# 前回と時間が同じならステータスを更新
if (lasttime == now):
print("タッチ重複のため更新しません")
con.commit()
else:
c.execute("update Felica set time='%s' where IDm='%s'" % (now, idm))
if status == "offline":
c.execute("update Felica set status='%s' where IDm='%s'" % ("online", idm))
print("onlineになりました")
else:
c.execute("update Felica set status='%s' where IDm='%s'" % ("offline", idm))
print("offlineになりました")
con.commit()
if __name__ == '__main__':
cr = MyCardReader()
de = DatabaseEdit()
while True:
print ("カードをタッチしてください")
cr.read_idm()
#16進数16桁の文字列にする
new_idm = str(cr.idm)
new_idm = new_idm[2:-1]
# print ("カードのID: ",end='')
# print (new_idm)
# 更新
de.updatetime(new_idm)
# Ctrl-Cで終了するまで待機し続ける
signal.signal(signal.SIGINT,signal.SIG_DFL)
実行結果
カードをタッチしてください
読み取り中
tanakaさん
onlineになりました
カードをタッチしてください
読み取り中
suzukiさん
offlineになりました
カードをタッチしてください
読み取り中
suzukiさん
タッチ重複のため更新しません
工夫したこと
連続でタッチしてしまったとき、statusが書き換わってしまいます。そこで、time(12:20などの文字列)が一致しているときは書き換えないようにしました。
課題
-
SQLiteの理解
もう少しスッキリした書き方ができそう。型はちゃんと見直すつもりです。
-
連続タッチ回避
ちょうど1日経ったときや、ちょうど分が変わるときにタッチしてしまうと意図しない結果になります。30秒たったらという処理にしようかと思いましたが面倒でした。より良い方法があれば直したいです。