nfcpyにおけるタイムアウト処理
nfcpyを利用してカード情報を読み取る際のタイムアウト処理について考えます。
nfcpy
nfcpyと非接触型のデバイスを使って、NFC対応のカードから情報を読み取ることができます。
今回はPaSoRiのRC-S380を利用しています。対応デバイスは公式を確認してください。
カード情報を読み取る簡単なコードは以下のようになります。
import nfc
with nfc.ContactlessFrontend('usb') as clf:
clf.connect(rdwr={})
これだとカードの読み取りが行われるまで(カードが置かれ、離されるまで)待ち続けてしまいます。
nfcpyにおいて、以下のように実装されていることで読み込み待機が発生しています。
# nfc/clf/__init__.py
class ContactlessFrontend(object):
...
def connect(self, **options):
...
terminate = options.get('terminate', lambda: False) # (1)
...
try:
while not terminate(): # (2)
if rdwr_options:
...
...
(1) デフォルトのterminateはlambda: Falseと設定されています。
(2) これにより、terminate()がFalseを返し続け、永遠に読み取りを行おうとします。
timeout
つまり、connectの引数にterminate=を与えることで、タイムアウト処理を実装できます。
公式のサンプルを参考に、5秒後に待機をやめるようにするには以下のようになります。
nfcpy : nfc.clf.ContactlessFrontend.connect
from functools import partial
import time
import nfc
def afrer(n, started):
return time.time() - started > n
with nfc.ContactlessFrontend('usb') as clf:
started = time.time()
wait_s = 5
clf.connect(
rdwr={},
terminate=partial(afrer, wait_s, started)
)
”読み取りを開始した時間(started)から、5秒後(wait_s)にTureが返るような関数”をterminateに渡すことでタイムアウト処理を実装することができました。
これだとカードを置いている状態でも、タイムアウトが実行されてしまいます。
そこで、カードが置かれている場合はタイムアウト処理を停止させます。
class Card(object):
def __init__(self):
self.on_card = False
def on_connect(self, tag):
self.on_card = True
return True
def on_release(self, tag):
self.on_card = False
return True
def after(self, started, n):
return time.time() - started > n and not self.on_card
def __call__(self, started, n):
with nfc.ContactlessFrontend('usb') as clf:
rdwr_options = {
'on-connect': self.on_connect,
'on-release': self.on_release
}
clf.connect(
rdwr=rdwr_options,
terminate=partial(self.after, started, n)
)
card=Card()
card(time.time(), 5)
'on-connect'と'on-release'のオプションを追加してあげることで、カードの読み込み開始と終了を確認できます。
これでon_cardがTrueの際(カードが置かれたままの状態)には、タイムアウト処理が停止します。
外部制御
このterminateを利用することで、別の場所から読み込み待機を解除することが可能になります。
import threading
import time
import nfc
class Card(object):
def __init__(self):
self.flag = False
def __call__(self):
with nfc.ContactlessFrontend('usb') as clf:
clf.connect(rdwr={}, terminate=lambda: self.flag)
threading.Thread(target=(card:=Card())).start()
time.sleep(5)
card.flag=True
外からflagをTrueにすることで、読み込み待機を解除できました。
この実装により、カード読み込みとカード登録などを一台の非接触形のデバイスでカバーできるようになります。