今回は、GMOバーチャルジムで稼働しているWindows PCを RaspberryPi4に移行する作業、おもにトレッドミルコントローラという .NET Framework 4.6 プログラムの移植作業についてご紹介します
GMOバーチャルジムとは?
GMOインターネットグループが開発した【GMOバーチャルジム】は、180°全面ディスプレイに各大会のコースの映像を流しながら走ることで実際にレースを走っている感覚を味わうことが出来るトレーニングマシンですコース合わせてトレッドミルの傾斜が自動で調整され、気温・風・酸素濃度も自由に設定することができることから、大会前のコースの試走や、低酸素トレーニングに活用されていますhttps://athletes.gmo.jp/about/ より
トレーニングルームの様子
今回は、このシステムで利用しているPCの入れ替え時期が近くなったため、WindowsPCから RaspberryPi4に移行することにしました
WindowsPCで動いているもの
今回移行の対象となった WindowsPCでは 以下のものがうごいていましたトレッドミルコントローラ (今回の記事の対象です)
トレッドミルの管理を行っていますWindowsサービスとして動いています
マネージャーサイト
設備の予約管理や、トレーニングの履歴管理等、設備全体の管理を行っているWebサイトですDockerのコンテナとして動いています
監視ツール
各種機器の監視をし正常に動作しているかのチェックを行っていますDockerのコンテナとして動いています
Windowsから移行するにあたり トレッドミルコントローラが移行の主なハードルになっていました
トレッドミルコントローラで行っていること
USBシリアル変換ケーブルを用いたシリアル通信でトレッドミルの制御HTTP Serverでマネージャーサイトからのコマンドの受付UDPでマネージャサイトや他の機器にトレッドミルの情報の送信
なんでRaspberryPi4?
WindowsPCはWindows Update等で設定が変わってしまうことがあり、メンテナンス作業に時間がかかってしまっていましたRaspberry Pi4 は メモリも増えて早くなり、USBのSSDを接続すればSDカードの寿命からも開放され普通のLinuxマシンとして利用できます、最近はARMのバイナリも豊富になり昔より自分でビルドをする必要も減っています
また 昔は手動で行っていた OverlayFSをつかってSDカードを読み取り専用にする等の小技は今のRaspberry Pi OS では標準でサーポートされ簡単に設定できるようになっています
Type-Cコネクタを利用したOTGでPCから給電しつつPCとネットワーク接続が可能です、PC側でテザリングの設定を行うとRaspberryPiからPCを経由してインターネット接続をしたり、RaspbeeryPiでトンネル接続を作成してPCからそのトンネル経由で接続する等も可能です開発時はUSBケーブル1本で開発を行っていました。別途電源を用意したりする必要もLANケーブルも必要なく気軽に使えます
Raspberry Pi 5ではなく4にしたのは、単純にRaspberry Pi 5の発売前に調達したためです。今でもRaspberyPi4にするかもしれないです、5は熱いので…
奥にある黒いものがUSBのSSDです
移行開始時点では、私のC#の経験はUnityで使っていた程度で、 .NET Framework や .NET Coreを十分に理解していなかったため、まず .NET Framework を Linuxで動かすための調査から始めました
そこで 4.6等も含めてリリース年ごとに並べました
.NET VersionRelease Year機能強化と新機能.NET Framework 4.02010並列コンピューティングManaged Extensibility Frameworkアプリケーションの互換性と配置.NET Framework 4.52012非同期のファイル操作Windows 8.x ストア アプリ用 .NET.NET Framework 4.62015マネージド コードの JIT コンパイラ (64 ビット)ASP.NET Core.NET Core 1.02016マルチプラットフォームサポートオープンソース.NET Framework 4.72017楕円曲線暗号 (ECC) による機能強化高 DPI のサポートオブジェクト キャッシュの拡張性.NET Core 2.02017.NET Standard 2.0 のサポート.NET Framework 4.82019ZLib の更新バージョンの使用ServiceHealthBehavior の導入JIT コンパイラの機能強化.NET Core 3.02019Windows デスクトップ アプリケーションのサポート.NET 5.02020パフォーマンスの向上ダンプ デバッグでのツールのサポート.NET 6.02021Arm64 のサポート長期サポート (LTS).NET 7.02022パフォーマンスジェネリック型数値演算
.NET Coreと.NET Framework 4.6以降が同じようなタイミングでリリースされてることがわかります。.NET CoreはWindows以外の環境でも動くようにしたマイクロソフトによるクロスプラットフォームかつオープンソースのリファレンス実装ということで、同時期にリリースされた .NET Frameworkとある程度の互換性が保たれているようでした
.NET Core 4 が存在せず、.NET 5 になっているのは .NET Framework 4.x との混同を避けるためにスキップされました
というわけで、移植するだけならば .NET Framework 4.6から .NET Core 1.0がバージョンが近いので楽そうですが、2019年6月27日にサポートが終了しています
ちょうど実装を始めたころに .NET 8がリリースされたので それを使うことにしました
.NET Framework 4.6から.NET 8へのアップグレードでおよそ8年分いきなり新しくすることになりました.NET 8がLinxuでシリアルポートを扱えない場合は移行が不可能になりますが少し調べただけでも使えるということがわかり割と互換性は担保されているようです
RaspberryPiはuartを持っています、また今使っているUSBシリアル変換ケーブルもそのまま動くことが期待できます。現在の配線をそのまま利用したいので、USBシリアル変換ケーブルを利用することにしました
移植作業
今回の移行では.NET8にすると同時にコンテナ化して、Dockerで動かすことにしましたコンテナ化も .NETの標準機能で簡単に行えました Dockerfileを自分で用意する必要もないです
実際の移植作業ですが、プロジェクトファイルを新規で作成することにしました.NET8のプロジェクトを作成してビルドを行うといくつかのエラーが出ましたが、想像していたよりもエラーが少なくてびっくりしました
主なエラーの内容は以下の3つです- Windowsサービス等Windowsの機能に依存しているもの- インスタンスのDeepCopyで利用していた BinaryFormatterが非推奨に- 外部ライブラリが足りない
外部ライブラリのインストールやエラーの書き直しを行いながらビルドを行いますエラーがなくなりビルドが成功するまでくりかえしますが、過去に行った移植作業等と比べても難しいものではありませんでした
同時にコンテナ化に伴う修正としてLog4netの出力先を標準出力に変更しましたConfigurationTransformで設定ファイルをビルド時に書き換えていたのを環境変数を参照するようにも変更しています
公式のドキュメントが見やすく整備されているので特にトラブルに遭遇することなくエラーの解消や変更点の実装が行えました
利用しているライブラリは以下になりました
Dynamic.Json 1.4.0 1.4.0log4net 2.0.15 2.0.15Microsoft.Extensions.Configuration 8.0.0 8.0.0Microsoft.Extensions.Configuration.CommandLine 8.0.0 8.0.0Microsoft.Extensions.Configuration.EnvironmentVariables 8.0.0 8.0.0Microsoft.Extensions.Configuration.FileExtensions 8.0.0 8.0.0Microsoft.Extensions.Configuration.Json 8.0.0 8.0.0Microsoft.Extensions.Hosting 8.0.0 8.0.0Microsoft.Extensions.Hosting.Abstractions 8.0.0 8.0.0Microsoft.NET.Build.Containers 8.0.200 8.0.200Microsoft.Windows.Compatibility 8.0.0 8.0.0System.Text.Encoding.CodePages 8.0.0 8.0.0
動作確認
トレッドミルが無いと動作確認できないのでデバッグ等は現地で行っています
最終確認の様子
起動や、マネージャーサイトとの疎通等は問題ありませんでしたが、トレーニングを開始したところシリアル通信がうまく行っていないようでトレッドミルが動かない状態でしたトレッドミルとの通信は iso 2022-jp を利用しており文字コードの変換が失敗しているようでした
テキストエンコードは以下に詳細があり、 .NET5以降はサポートにチェックが入っていませんでしたhttps://learn.microsoft.com/ja-jp/dotnet/fundamentals/runtime-libraries/system-text-encoding
NuGetパッケージ<System.Text.Encoding.CodePages>を追加すればShift JIS 等を利用できることがわかりパッケージの追加と以下の1行をソースコードに追加で対処が可能なようです
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
この変更で問題なく動作するようになりました。無事 .NET Framework 4.6からLinuxの .NET 8へと移行することができました。コマンドを投げるとトレッドミルの傾斜が変わったり速度が変わったりするのは楽しかったです
おまけ:Raspberry4 のUART
RaspberryPi4 では 6つのUARTを持っています、RaspberryPi3/Zero等その他のシリーズは2つのUARTを持っています使う時に注意が必要なmini UARTのuart0/1は以外の空いているUARTを利用することができますがI2CやSPIとピンが競合していますUARTとGPIOピンの関係は以下のようになっていました
UARTGPIOピン番号ttyuart0GPIO 14,15 32,33 or 36,37/dev/ttyAMA0uart1GPIO 14,15 32,33 or 36,37/dev/ttyS0uart2GPIO 0,1 (default off)/dev/ttyAMA2uart3GPIO 4,5 (default off)/dev/ttyAMA3uart4GPIO 8,9 (default off)/dev/ttyAMA4uart5GPIO 12,13 (default off)/dev/ttyAMA5
pinoutコマンドの結果