エッジ端末が増えてつらくなってきたので、PLC操作まわりを分けた話
工場の中で、小型デバイスや測定機まわりから三菱PLCへつなぐ仕組みを作ることがあります。M5Stack みたいな小さい端末を使うこともあるし、Python でちょっとしたスクリプトを書くこともあります。
そんなことをやっていると、実装そのものと同じくらい悩ましくなってくるのが、PLC 操作まわりをどう配るか、どう管理するかでした。
最初は定番の pymcprotocol にかなりお世話になっていました。Python から三菱PLCを触る入口としては扱いやすかったです。
ただ、エッジ端末が増えてくると話が変わりました。PLC と直接通信するコードを各クライアントに持たせる形だと、端末ごとに PLC への到達性や接続先を考える必要が出てきて、ポート開放やネットワーク設定の管理コストがだんだん上がってきました。
そのあたりを整理していく中で、まずは PLC と直接話す通信層そのものを自分で持った方が考えやすいと思い、gomcprotocol を作る方向に寄っていきました。
そこで、PLC と直接話す層、REST API として公開する層、Python からその API を使う層は分けた方がよさそうだと考えるようになりました。
そこで整理したのが、gomcprotocol、gomc-rest、gomc-rest-client の3つです。
この記事では、単に「3つに分けました」という話ではなく、なぜその分け方が閉域環境への配布を楽にしたのかを中心に書きます。
ざっくりした構成はこんなイメージです。
3つの役割
| プロジェクト | 役割 | 主な利用者 | 使いどころ |
|---|---|---|---|
| gomcprotocol | PLC と直接通信する層 | Go で組み込む側 | MC Protocol を直接扱いたいとき |
| gomc-rest | PLC 操作を REST API として公開する層 | エッジ端末側、他クライアント | PLC 接続やポート管理を集約したいとき |
| gomc-rest-client | gomc-rest 専用の Python クライアント層 | Python スクリプト利用側 | REST API を Python から手早く扱いたいとき |
先にリポジトリへのリンクを置いておきます。
- gomcprotocol: https://github.com/Moge800/gomcprotocol
- gomc-rest: https://github.com/Moge800/gomc-rest
- gomc-rest-client: https://github.com/Moge800/gomc_rest_client
- gomc-rest-client on PyPI: https://pypi.org/project/gomc-rest-client/
まず gomcprotocol と gomc-rest を分けた
最初の土台は gomcprotocol です。これは三菱PLCと MC Protocol で直接通信する Go ライブラリで、3E/4E フレーム、read/write、remote control を扱えます。
もちろん「組み込みなら C/C++ で組め」と言われそうな気はします。ただ、そのときの自分は「Go は通信まわりに強いらしい」という、わりとミーハーな動機と勉強がてらで Go を選びました。
実際に組んでみると、Go は通信まわりの実装がかなりシンプルに済みました。
リポジトリ: https://github.com/Moge800/gomcprotocol

もちろん Go でがっつり組むならこれでいいのですが、Python を使いたい側から見ると、そのまま使うには少し距離があります。
最初は「PLC を REST API 越しに触れれば便利そう」くらいの感覚でした。ただ、この層をそのまま利用側に見せると、利用者は PLC 通信の前提や実装都合をかなり直接意識することになります。しかもエッジ端末が増えるほど、各端末から PLC へ直接つなぐ前提そのものが重くなります。
端末ごとにポート開放や疎通確認をする運用は、たぶんすぐにつらくなります。そこで PLC 操作を REST API として公開する gomc-rest を別に作りました。ワード単位だけでなく、D100.1 のようなビット単位の操作もこの側で扱えるようにしています。
リポジトリ: https://github.com/Moge800/gomc-rest
実際に運用へ寄せていくと、gomc-rest は単なる API ラッパーというより、PLC 接続を集約する gateway のような立ち位置になっていきました。
gomc-rest を挟むと、利用側は PLC 通信そのものではなく REST API を相手にすればよくなります。 各エッジ端末から PLC へ直接つなぐのではなく、gomc-rest が PLC 側との通信をまとめて持つことで、利用側は REST API だけを意識すれば済むようになります。ここで責務はかなり整理されました。
- gomcprotocol は PLC と直接通信する層
- gomc-rest は PLC 操作を REST API として公開する層
要するに、PLC と直接つながる口を各エッジ端末にばらまくのではなく、gomc-rest という 1 つの窓口に寄せる形です。これなら利用側は REST API で扱えますし、PLC 側の接続やポートまわりの管理も集約しやすくなります。
その結果、少なくとも利用側には次のようなものを配らなくてよくなりました。
- PLC 側の接続先変更
- ポート管理
- 疎通確認
- PLC 通信実装の差異
もちろん、各言語には PLC と直接つなぐための扱いやすいライブラリがあります。ただ、今回はライブラリ選定というより、直結前提を各端末に持たせない構成にしたかった、というのが主眼でした。
特に、閉域環境で小型端末や Python スクリプトが増えていくと、「PLC と直接通信する責務をどこへ置くか」はかなり効いてきます。結果的に gomc-rest は、PLC 通信を各端末へばらまかないための gateway として振る舞うようになりました。
それでも gomc-rest-client は欲しかった
gomc-rest の API を Python からそのまま叩くだけでも動きます。ただ、実際に使う側からすると、毎回エンドポイントやパラメータ、エラーハンドリングを意識するのはやっぱり少しつらいです。
それに加えて、今回かなり重要だったのが配布のしやすさでした。閉域環境では、依存パッケージが増えるほど持ち込みと検証の手間が増えます。Python で使いやすいだけでなく、持ち込みやすい形で配れることが必要でした。
そこで gomc-rest 専用の Python クライアントとして gomc-rest-client を切り出しました。
リポジトリ: https://github.com/Moge800/gomc_rest_client
- gomc-rest-client は gomc-rest 専用の Python クライアント層
- PLC とは直接通信せず、REST API を Python から扱いやすくする
- 配布単位を分けることで、利用者に必要なものだけを渡しやすくした
ここは使い分けもはっきりしています。
- M5Stack みたいなマイコンや小さい端末から使うなら、gomc-rest を踏み台にして REST API で触る
- もう少し本格的に Python でスクリプトを書くなら、gomc-rest-client を入れて手早く組む
- Go でしっかり組み込むなら、gomcprotocol を内包して直接扱う
閉域環境に配りやすくするためにやったこと
一番効いたのは、gomc-rest-client を標準ライブラリだけで動くようにしたことです。
最初は requests を使う形でもよかったのですが、閉域環境へ持ち込むことを考えると、依存が1つ増えるだけでも配布と検証の手間が増えます。ネットワークに出られない現場では、pip install requests ひとつでも、その wheel を別で持ち込むのか、依存をどこまで事前に固めるのか、検証対象をどこまで増やすのかを考える必要があります。
しかも requests だけで終わるわけではなく、依存が増えれば「現場へ何を持っていくか」「その場でどこまで説明するか」も増えていきます。工場や閉域網の運用では、この 1 つ増える、が地味に重いです。そこで requests 依存を外し、ランタイム依存なしで動くように整理しました。
これで何がよくなったかというと、wheel をそのまま持ち込みやすくなったことです。利用者は PyPI からインストールしてもいいし、ネットワークに出られない環境なら事前に作っておいた wheel を持ち込んでインストールしてもいい、という形にできます。
実際、Python クライアント利用を前提にすると、閉域環境に持ち込むものをかなり単純化できました。 サーバー側は Windows なら gomc-rest.exe、Linux なら gomc-rest の実行バイナリ、Python 利用側は gomc_rest_client-*.whl を持っていけばよく、基本的にはその 2 つを配れば済む形です。
PyPI から入れる場合は、普通に pip でインストールできます。
pip install gomc-rest-client
ネットワークに出られない環境へ wheel を持ち込む場合は、事前に PyPI や GitHub Releases から whl ファイルをダウンロードしておき、それを現場 PC へ持ち込んでインストールします。
pip install dist/gomc_rest_client-*.whl
この形にしておくと、閉域環境向けの説明もかなりシンプルになります。配る側としても、追加のパッケージとその依存地獄を気にしなくてよいのはかなり大きいです。
まとめ
gomcprotocol、gomc-rest、gomc-rest-client を分けた理由は、単に実装を整理したかったからだけではありません。
PLC と直接話す層、REST API として公開する層、Python から扱う層を分けることで、利用者に渡す責務を絞れました。特に gomc-rest は、単なる API 化のための層というより、PLC 接続を集約して各端末へ直結責務を配らないための gateway として効いています。その結果として、gomc-rest-client は標準ライブラリだけで動き、PyPI でも wheel 持ち込みでも配れる形にできました。
閉域環境では、動くことと同じくらい、無理なく配れることが重要です。 今回 3 つに分けたのは、その配布しやすさを実運用の中で取りにいった結果でもありました。
最初の叩き台は AI と対話しながらかなり早く作れました。ただ、実際に現場で使える形にしていくには、責務の分け方や閉域環境での配布方法まで含めて整理する必要がありました。自分としては、早く作れたことよりも、最終的に無理なく運用できる形まで持っていけたことの方が大きかったです。

