Raspberry Piで自宅の鍵をオートロック化してみたの続編です。ハード的にはほぼ変わりませんが、HTTP通信はオーバーヘッドが大きくて無駄が多いので、D-busで作り直しました。
D-busはLinuxのプロセス間通信の1つで、初めて使ったのですが、非常に便利で、パフォーマンスも大幅に改善されました。
前回までの反省
- 距離センサーの測定を高速(0.5秒おき)で行うと、たまに測定が狂う。(TRIGが一瞬で立って、短距離と誤測定する)。SR04上のICがついて来れないのかなと思いました。
- HTTP通信のオーバーヘッドが無駄。TCPコネクションが無駄。FastAPIが無駄。
- RFIDセンサーを0.1秒ごとに初期化して読み取るため、計算が重い
また、鍵を自室に忘れて部屋を出ると、閉め出されるので、ポカヨケとして、鍵を取ったら鍵が開くようにしました。
パフォーマンスの変化
アクチュエータやセンサーをマイクロサービスとして、設計するという発想は非常に気に入っているため、HTTP通信をD-busに変更して、再設計しました。
今回の変更による使用リソースの変化です。もともと、前回のシステムでは、RFIDの読み取り(PID=2087)が、26%も食っていたのですが、2%まで落ちました(PID=836)。
%Cpuもload averageもMemもすべて改善しています!!
##Before
方式設計
それでは具体的な設計についてお話しましょう。
状態遷移
前回とほぼ同じです。ポカヨケで、鍵を外したら、鍵が開くようにします。
クラス設計
各モジュールをD-busサーバーとして立て、オートロックシステム本体は、D-busクライアントとして、制御します。
前回と同様にStateパターンで書いていますが、前回と異なるのは、定期的に状態を更新する関数を呼び出すのではなく、wait_for_next_state関数にて、イベントループを回すことで、処理のオーバーヘッドを減らしています。
コーディング
サーバーはこちらにあります。
オートロックシステムはこちらにあります。
デプロイメント
Sessionバスを使用するため、loginctl enable linger
コマンドによってブート時やログアウトした後でも、daemonとして動作するようにします。Starting systemd services sharing a session D-Bus on headless systemが非常に参考になりました。
上のリンクを参考にして、以下のように.serviceファイルを作成し、systemctl --user enable auto_lock_dbus
でデプロイ完了です。
[Unit]
Description = RC522
Requires=dbus.socket
[Service]
WorkingDirectory = /home/baki/development/dbus_server
ExecStart=/home/baki/.pyenv/shims/python3 -u rc522_server.py
Restart=always
Type=simple
EnvironmentFile=/etc/sysconfig/dbus_env
[Install]
WantedBy=default.target
[Unit]
Description = DS3225
Requires=dbus.socket
[Service]
WorkingDirectory = /home/baki/development/dbus_server
ExecStart=/home/baki/.pyenv/shims/python3 -u ds3225_server.py
Restart=always
Type=simple
EnvironmentFile=/etc/sysconfig/dbus_env
[Install]
WantedBy=default.target
[Unit]
Description = SWITCH
Requires=dbus.socket
[Service]
WorkingDirectory = /home/baki/development/dbus_server
ExecStart=/home/baki/.pyenv/shims/python3 -u switch_server.py
Restart=always
Type=simple
EnvironmentFile=/etc/sysconfig/dbus_env
[Install]
WantedBy=default.target
[Unit]
Description = LINE
Requires=dbus.socket
[Service]
WorkingDirectory = /home/baki/development/dbus_server
ExecStart=/home/baki/.pyenv/shims/python3 -u LINE_server.py
Restart=always
Type=simple
EnvironmentFile=/etc/sysconfig/dbus_env
[Install]
WantedBy=default.target
[Unit]
Description = AUTO_LOCK
After=ds3225_dbus.service rc522_dbus.service switch_dbus.service line_dbus.service
Requires=dbus.socket ds3225_dbus.service rc522_dbus.service switch_dbus.service line_dbus.service
[Service]
WorkingDirectory = /home/baki/development/auto_lock_dbus
ExecStart=/home/baki/.pyenv/shims/python3 -u auto_lock.py
Restart=always
Type=simple
EnvironmentFile=/etc/sysconfig/dbus_env
[Install]
WantedBy=default.target
テスト
dbus-monitorというコマンドがあって、D-busの通信を覗き見る事ができます。RC522ServerはRFIDを検知すると、シグナルを発生するので、RC522Server実行中に、以下のコマンドを実行すると、D-busにRFIDが流れていることが確認できます。
dbus-monitor "type='signal',sender='jp.kimura.RC522Service',interface='jp.kimura.RC522'"
反省
今回、dbus-pythonというライブラリを使用したのですが、このライブラリはイベントループにGLibを使用します。GLibはPyPyでインストールすると依存関係を解決できず自前でビルドする必要があり、非常に面倒だったので、aptでインストールして、システムのPythonで動かすハメになりました。
しかし、後から気がついたのですが、DBus-Nextというライブラリは、asyncioに対応しているみたいなので、そっちのほうが良かったかもしれません。
参考文献
DBus ことはじめ
D-Bus のはなし
GitHub dbus-python/examples/
Starting systemd services sharing a session D-Bus on headless system