9.4. リンクを確立する¶
前のページで取り上げたリンク層はほとんど自動的に処理されますが、1か所だけPythonスクリプトが介入しなければならない箇所があります。それは、カメラにどのネットワークに参加するかを指示することです。このステップが成功するまで、このセクションの残りで取り上げるネットワーク機能はどれも動作しません。
9.4.1. network モジュール¶
network モジュールは、カメラのネットワークハードウェアをPythonに公開します。利用できるインターフェースの正確な構成はボードによって異なります。多くのカメラは無線チップを備えており、WLAN クラス(Wireless Local Area Network にちなんで命名)を公開します。一部のボードは内蔵のイーサネットポートも備えており、LAN クラス(Local Area Network、すなわち有線版にちなんで命名)を公開します。使い方のパターンは両者で同じですが、1つ重要な違いがあります。無線インターフェースはどのネットワークに参加するかを指示する必要があるのに対し、イーサネットはケーブル上にあるものを何でも拾い上げます。
9.4.2. Wi-Fiの流れ¶
Wi-Fiネットワークへの参加は3つのステップから成ります。インターフェースを構築し、それを起動し、パスワードとともに名前付きのネットワークへ接続するよう要求します。インターフェースはバックグラウンドでアクセスポイントとネゴシエートします。isconnected() の呼び出しは、リンクの確立が完了したときにそれを報告します:
import network
import time
wlan = network.WLAN(network.WLAN.IF_STA)
wlan.active(True)
wlan.connect("my-network", "my-password")
while not wlan.isconnected():
time.sleep_ms(100)
print("link up")
引数 IF_STA は ステーション モードを選択します。これはカメラが、誰か別の人がホストしているネットワークに参加するモードです。反対のモードである IF_AP は、カメラが他のデバイスから参加できる独自の小さなネットワークをホストするようにします。設定インターフェースや現地でのセットアップには有用ですが、一般的なケースではありません。
isconnected() が True を返すと、カメラはネットワーク上にあります。上位層が自身をセットアップするために必要としたその他のすべては、リンクの確立中に自動的に行われています。以降のページでは、それらの要素を一つずつ詳しく説明します。
9.4.3. 起こりうる問題¶
このステップでは、いくつかの実際的な失敗のパターンが現れます。
ネットワーク名またはパスワードの誤り。 接続の試行は、アプリケーションが諦めるまで黙って再試行を続けます。上記のループが永遠にブロックしないよう、待機処理をタイムアウトで包んでください:
start = time.ticks_ms() while not wlan.isconnected(): if time.ticks_diff(time.ticks_ms(), start) > 10000: raise OSError("Wi-Fi did not come up in 10 s") time.sleep_ms(100)
範囲外。 カメラとアクセスポイントは、リンクを保持できるだけの十分に強い信号が得られる程度に近くなければなりません。
status()はリンクが確立しない理由を示すコードを返します。scan()は無線が検出できるネットワークの一覧を返し、これはconnectが成功しないときに実行すべき診断手段です。アクセスポイントがパスワード以上のものを要求する。 オープンなネットワーク(パスワードなし)と一般的なパスワード保護されたネットワークは、上記で示したとおり
connectでカバーされます。職場や学校にある規模の大きいネットワークでは、カメラが別個のログインサーバーに対して認証しなければならない異なる方式が使われることがあります。それらにはconnectへの追加の引数が必要です。すべての詳細については class WLAN -- 内蔵WiFiインターフェースの制御 を参照してください。
9.4.4. 接続を維持する¶
リンクを確立することは問題の半分にすぎません。接続を維持することがもう半分です。アクセスポイントは再起動し、カメラは範囲外へ移動し、DHCPのリースは期限切れになり、無線ファームウェアは時折スタックします。何か月もネットワーク上に置かれるカメラは、それを検知して自力で復旧しなければなりません。
検知のパターンは、メインループの繰り返しごとに isconnected() を一度呼び出し、それが False を返したときに対応することです。isconnected() は、無線がまだ気づいていないうちに接続が切断された場合、短時間だけ誤った値を返すことがあります。リンクが「確立しているはず」のときに失敗するソケット送信は、切断を示すアプリケーション側のもう一つの証拠です。status() は、両者が食い違ったときにより信頼できる情報源です。
再接続のパターンは、disconnect() に続けて同じ資格情報で connect() を呼び出すことであり、待機処理は初回接続と同様にタイムアウトで包みます。試行の間にはバックオフを入れます。1秒、2秒、4秒と、1分程度まで倍々にしていきます。こうすることで、長時間の障害がアクセスポイントを酷使せず、また無線の電力を空回りのループで浪費しないようにします:
import network
import time
_BACKOFF_S = (1, 2, 4, 8, 16, 32, 60)
def reconnect(wlan, ssid, password):
for delay in _BACKOFF_S:
wlan.disconnect()
wlan.connect(ssid, password)
deadline = time.ticks_add(time.ticks_ms(), 10_000)
while not wlan.isconnected():
if time.ticks_diff(deadline, time.ticks_ms()) < 0:
break
time.sleep_ms(100)
if wlan.isconnected():
return True
time.sleep(delay)
return False
そのヘルパーが False を返し続ける場合、無線ファームウェアそのものが行き詰まっている可能性があります。最後の手段は無線の電源を入れ直すことです。active(False)、短い一時停止、active(True)、そして最初から再接続します。これは数秒の追加のダウンタイムと引き換えに、無線ファームウェアを既知の状態に戻します:
def radio_power_cycle(wlan, ssid, password):
wlan.active(False)
time.sleep(1)
wlan.active(True)
return reconnect(wlan, ssid, password)
数分間ネットワークから切断されたままのカメラは、アプリケーションが認識しなければならない実際の障害です。復旧コードはその状態を表面化させるべきです。すなわち、メインループがチェックするフラグでネットワークを不健全としてマークし、そのフラグがクリアされている間はアプリケーションが行うはずだったネットワーク送信をスキップさせます。こうすることで、長時間の障害が、決して書き込めないソケットを待ってアプリケーションを停止させてしまうことを防ぎます。
9.4.5. イーサネット(搭載されている場合)¶
イーサネットを内蔵したボードは、connect のステップなしで同じパターンを公開します。LAN インターフェースは active() で起動され、ケーブルが差し込まれるとすぐにインターフェースは使用可能になります:
import network
lan = network.LAN()
lan.active(True)
print("link up")
この時点以降、このセクションの残りのページは、どのインターフェースがカメラをネットワークに参加させたかにかかわらず、同じように適用されます。上位層は、その下にあるリンクがWi-Fiであるかイーサネットであるかを気にしません。「接続済み」は「接続済み」です。
WLAN および LAN の完全なリファレンス、ここに収まりきらなかった設定項目を含めて、については network --- ネットワーク構成 を参照してください。