FelicaのIDmを読み取ってSQLite3で管理する

## 前の記事

この記事の続きになります。

[Python3 でFelicaのIDmを読み取って記録する](https://qiita.com/depretiger/items/eb5c84faa59ee5743962)



## 実現したいこと

入退室管理のようなことをしたいと思っています。

前回FelicaからIDmを読み取ることができたので、今回はFelicaを事前に登録し、タッチを繰り返すことでonlineとofflineを切り替えるようなものを作ります。



##SQLite3を使ってみる

全く知識がなかったので以下の記事を参考にしました。SQLの文法は随時検索して学びました。

[【python】データベースの入門としてSQLiteを使ってみた](https://www.htmllifehack.xyz/entry/2018/08/03/231351 )



### テーブルを生成する

テーブル名はFelica、カラムは name、 IDm、 time、 statusにしました。型が適当になってしまったのは改善したいと思っています。textで良かったんでしょうか。

```python
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()
```

### レコードを追加する

```python
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()
```



### テーブルの中身を確認する

```python
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で終了するまで待機し続ける

```python
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秒たったらという処理にしようかと思いましたが面倒でした。より良い方法があれば直したいです。