地震情報アプリ界隈 Advent Calendar 2021 14 日目の記事です。
P2P 地震情報 Windows 版を 10 年ぶりに更新しました。デザインは一新しましたが、目立った新機能はなく、未実装の箇所も残っています。
しかし、実は半分くらいがクロスプラットフォームになり、そのコードは Linux 上で動いていたりします。今回は、そのアーキテクチャをご紹介したいと思います。
P2P 地震情報とは
P2P 地震情報は、気象庁の地震情報・津波予報と、ユーザ同士の「揺れた!」という情報を P2P ネットワークで共有する無償のサービスです。
Windows 版のほか、 iOS 版、 Android 版、 Twitter @p2pquake 、さらに開発者向けに JSON / WebSocket API も提供しています。
旧 Windows 版の問題点
旧 Windows 版 (Beta3) は Visual Basic 6.0 で作っていました。運用を続けるうちに徐々に問題が生じてきました。
- 古すぎる。開発環境は 2008 年にサポート終了。実行は Windows 10 でも可能。
- マルチスレッドではない。 P2P 通信にはそもそも不向きである。
- API 提供にあたり、地震感知情報の収集のためだけに Windows 環境を常時運用する必要がある。
そこでリニューアルすることにしたわけです。
新 Windows 版のアーキテクチャ
新 Windows 版は、 Microsoft のソフトウェアフレームワーク .NET 5 で開発しています。もともと .NET Core と呼ばれていたもので、クロスプラットフォーム対応が特徴です。
開発・保守がしやすいよう 6 つのコンポーネントに分割していて、画面(ユーザインタフェース)を除く 5 つのコンポーネントがクロスプラットフォームに対応しています。
Web 版と地図は同じ
クロスプラットフォームであることの一例を見てみます。以下の画像は、 Linux サーバで動いている Web 版の地震情報地図と、新 Windows 版の地震情報地図を並べたものです。ほとんど同じです。 Linux でも Windows でも同じ地図生成のソースコードで動作しているためです。
アーキテクチャ詳解
ここからは少しだけアーキテクチャを詳しく見ていきます。
Client: サーバ通信、 P2P 通信、それらのコントローラ
コア部分にあたる Client は、 P2P 地震情報のピアとして動作するために、サーバ・ピア通信を担います。
デザインパターンの Mediator パターンと State パターンを組み合わせたようなクラス設計にしています。接続済の状態から切断するときは、ピアとの接続をすべて切断し、サーバに参加終了の通信をする… といった具合です。
地震情報などのデータを受信すると、下位クラスでイベントが発生し、上位へ運ばれ、最終的に最上位である MediatorContext クラスのイベントが発生します。画面にあたる WpfClient は、このイベントを処理して表示や通知をしています。
ソケットプログラミングは不安定で面倒なものです。接続済みかチェックしてデータを送信しても、送信する瞬間には切断されていることも珍しくありません。悲しいことに、ソースコードには try-catch が溢れています。
Map: ImageSharp によるクロスプラットフォーム描画
Map は地図生成を担います。といっても、地図生成には画像処理以外の要素も絡んでいます。
- 地図: 日本部分は地理院タイルを使用 (CC BY 4.0) 、それ以外は GSHHG のデータを用いて GMT で生成 (LGPL v3)
- 震度観測点: 気象庁 | 震度観測点 の情報を加工
- 震度速報地域の塗りつぶし: 気象庁 地震情報の GeoJSON データに基づき塗りつぶし範囲を決定(詳細: 地図を塗りつぶしたくて GeoJSON - Speaker Deck)
- 地震感知情報の塗りつぶし: 同上(気象庁の緊急地震速報や震度情報で用いる区域名と地震感知情報の地域名との対応は別途 Wiki ページに整理)
なお、クロスプラットフォームの描画ライブラリ SixLabors.ImageSharp を用いています。
WpfClient: WPF (Windows Presentation Foundation) による GUI
ユーザインタフェースを提供するのは WpfClient です。 ModernWpfUI を用いてモダンな見た目になっています。
モダンなのは見た目だけで、 WPF 自体はフレームワークとして「枯れた」部類に入っている気がします。 WinUI 3 や .NET MAUI がはるかにモダンです。ただ、ここで下手に躓いて開発が滞るのだけは避けたいという思惑がありました。
「 UI だけなら作り直しは簡単だから、まずは完成を」という気持ちで作りきったので、設計と呼べるものはあまりありません。 Program.cs は 400 行を超えて恥ずかしい感じになっています。ただ、クラスの依存方向が反転することが極力ないよう実装しています。
地震感知情報の地図生成については、 1 件ずつ生成していると受信ペースに追いつかないため、適宜生成を省略するように工夫しています。
選ばなかったアーキテクチャ
開発にあたっては、次のアーキテクチャも検討しましたが、最終的には選びませんでした。
- Electron: モダンなクロスプラットフォームとして最有力候補でしたが、既に安定稼働していた P2P 通信部分をきっちり移植できる自信がありませんでした。
- Electron.NET + Blazor (WebAssembly): P2P 通信部分がそのまま使えるものの、 ASP.NET Core が未知だったため躊躇しました。
- Client を gRPC サーバ化 + Flutter: Flutter デスクトップ対応があったので検討したものの、さすがに上記 2 案と比べても奇抜すぎて止めました。
クロスプラットフォーム対応の使いどころ
そんなわけで、半分ほどクロスプラットフォーム対応になっています。実は、 GUI 以外は既に Linux 環境で実際に運用しています。
- Map: Windows 版以外の地図画像を生成するために、 Linux サーバ上で実行
- Asn1PKCS: P2P 地震情報 サーバ (Linux 版) で利用
- Client, PKCSPeerCrypto: API 等で提供するための地震感知情報の収集に、 Linux サーバ上で実行
コンポーネント分割せずに失敗した過去
余談ですが、 Windows 版リニューアルは過去 2~3 度ほど試みていて、いずれも失敗していました。
野心的に新機能を盛り込みすぎたり、「一気に」作り直すやり方で進めていたりして、今振り返ると「そりゃ失敗しますよ」という感じがします。
今回は機能的にはかなり控えめにして、小さく分割して、「使えるところからどんどん動かしていく」という方法で進めました。少しずつでも「ちゃんと動いている」という安心感は、開発を前進させる力になりました。
YouTube コーディング配信
またまた余談ですが、開発の模様は一部 YouTube で配信していました。他人の開発風景ってなかなかまじまじと見る機会がないもので、ならば自分から、という感じでした。
1 ヶ月くらいで止めちゃいましたが、録画は続けていて 50 時間以上になっています。数分くらいに短くまとめて動画にしたいなと思っています(いつ完成するかはわからない)。
今後の課題
アーキテクチャや実装自体はかなり良くなりました。一方、機能としてはまだまだで、メモリ消費もかなり激しいです。
私も一利用者として使いつつ、完成度を高めていきたいと思います。
ソースコード
MIT ライセンスです。なんだかんだでそこそこのボリュームになりました(一部未コミットのコードを含みます)。
$ cloc . --include-ext=xaml,cs github.com/AlDanial/cloc v 1.82 T=68.71 s (5.7 files/s, 609.7 lines/s) ---------------------------------------------------------------- Language files blank comment code ---------------------------------------------------------------- C# 377 4986 4124 31773 XAML 18 41 16 952 ---------------------------------------------------------------- SUM: 395 5027 4140 32725 ----------------------------------------------------------------